├── .qmake.conf
├── LICENSE.GPL3
├── README
├── doc
├── config
│ ├── html-offline.qdocconf
│ ├── navigation.qdocconf
│ ├── qtota-online.qdocconf
│ ├── qtota-project.qdocconf
│ ├── qtota.qdocconf
│ └── style
│ │ └── qt5-sidebar.html
├── doc.pro
├── external-resources.qdoc
├── ota-module.qdoc
└── ota.qdoc
├── dracut
├── generate-initramfs
├── recovery
│ ├── module-setup.sh
│ └── recovery.sh
└── systemd
│ ├── 01-qt.conf
│ └── e2fsck.conf
├── examples
├── cpp
│ └── basic-daemon
│ │ ├── basic-daemon.pro
│ │ └── main.cpp
├── device-integration
│ ├── README
│ ├── beaglebone
│ │ └── uEnv.txt
│ ├── colibri-vf
│ │ └── uEnv.txt
│ ├── imx6qsabresd
│ │ └── boot.scr
│ └── nitrogen6x
│ │ ├── 6x_bootscript
│ │ └── no-initramfs
│ │ └── 6x_bootscript
└── qml
│ └── basic
│ ├── basic.pro
│ ├── main.cpp
│ ├── main.qml
│ └── qml.qrc
├── ota.pro
├── qt-ostree
├── build-ostree.sh
├── ostree-grub-generator
├── patches
│ ├── Allow-updating-files-in-the-boot-directory.patch
│ ├── Create-firmware-convenience-symlinks.patch
│ ├── Support-for-booting-without-initramfs.patch
│ ├── deltas-Expose-the-filename-parameter.patch
│ ├── deploy-add-karg-none-argument.patch
│ ├── ostree-prepare-root-enabler-for-simpler-kernel-arg.patch
│ └── u-boot-add-bootdir-to-the-generated-uEnv.txt.patch
└── qt-ostree
├── src
├── imports
│ ├── imports.pro
│ ├── pluginmain.cpp
│ └── qmldir
├── lib
│ ├── lib.pro
│ ├── qotaclient.cpp
│ ├── qotaclient.h
│ ├── qotaclient_p.h
│ ├── qotaclientasync.cpp
│ ├── qotaclientasync_p.h
│ ├── qotarepositoryconfig.cpp
│ ├── qotarepositoryconfig.h
│ └── qotarepositoryconfig_p.h
└── src.pro
└── sync.profile
/.qmake.conf:
--------------------------------------------------------------------------------
1 | load(qt_build_config)
2 |
3 | MODULE_VERSION = 1.0.0
4 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Snapshot of the latest documentation: http://doc-snapshots.qt.io/qtota/index.html
2 |
--------------------------------------------------------------------------------
/doc/config/html-offline.qdocconf:
--------------------------------------------------------------------------------
1 | include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf)
2 | include($QT_INSTALL_DOCS/global/fileextensions.qdocconf)
3 |
4 | HTML.nobreadcrumbs = "true"
5 |
--------------------------------------------------------------------------------
/doc/config/navigation.qdocconf:
--------------------------------------------------------------------------------
1 | url = https://doc.qt.io/qtota
2 | navigation.homepage = "Qt Documentation (Technology Preview)"
3 | navigation.landingpage = "Over the Air Update"
4 | navigation.cppclassespage = "Qt OTA Update C++ Classes"
5 | navigation.qmltypespage = "Qt OTA Update QML Types"
6 |
--------------------------------------------------------------------------------
/doc/config/qtota-online.qdocconf:
--------------------------------------------------------------------------------
1 | include(qtota-project.qdocconf)
2 | include($QT_INSTALL_DOCS/global/qt-module-defaults-online.qdocconf)
3 |
4 | # sidebar used for online template
5 | HTML.stylesheets += style/qt5-sidebar.html
6 | HTML.outputsubdir = "qtota"
7 | include(navigation.qdocconf)
8 |
--------------------------------------------------------------------------------
/doc/config/qtota-project.qdocconf:
--------------------------------------------------------------------------------
1 | include($QT_INSTALL_DOCS/global/qt-cpp-defines.qdocconf)
2 |
3 | project = QtOTA
4 | description = Qt OTA Update Documentation
5 | version = $QT_VERSION
6 |
7 | sourcedirs += .. ../../src/lib/ ../../src/imports
8 | headerdirs += ../../src/lib/
9 |
10 | indexes = $QT_INSTALL_DOCS/qtcore/qtcore.index
11 |
12 | qhp.projects = QtOTA
13 |
14 | qhp.QtOTA.file = qtota.qhp
15 | qhp.QtOTA.namespace = io.qt.qtota.$QT_VERSION_TAG
16 | qhp.QtOTA.virtualFolder = qtota
17 | qhp.QtOTA.indexTitle = Over-The-Air Update
18 | qhp.QtOTA.indexRoot = Over-The-Air Update
19 |
20 | qhp.QtOTA.subprojects = cpp qml
21 |
22 | qhp.QtOTA.subprojects.cpp.title = C++ Classes
23 | qhp.QtOTA.subprojects.cpp.indexTitle = Qt OTA Update C++ Classes
24 | qhp.QtOTA.subprojects.cpp.selectors = module
25 | qhp.QtOTA.subprojects.cpp.sortPages = true
26 |
27 | qhp.QtOTA.subprojects.qml.title = QML Types
28 | qhp.QtOTA.subprojects.qml.indexTitle = Qt OTA Update QML Types
29 | qhp.QtOTA.subprojects.qml.selectors = qmlmodule
30 | qhp.QtOTA.subprojects.qml.sortPages = true
31 |
32 | macro.B2Q = "Boot to Qt"
33 | include(navigation.qdocconf)
34 |
--------------------------------------------------------------------------------
/doc/config/qtota.qdocconf:
--------------------------------------------------------------------------------
1 | include(html-offline.qdocconf)
2 | include(qtota-project.qdocconf)
3 |
--------------------------------------------------------------------------------
/doc/config/style/qt5-sidebar.html:
--------------------------------------------------------------------------------
1 |
11 |
21 |
--------------------------------------------------------------------------------
/doc/doc.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = aux
2 |
3 | build_online_docs: {
4 | QMAKE_DOCS_TARGETDIR = qtota
5 | QMAKE_DOCS = $$PWD/config/qtota-online.qdocconf
6 | } else {
7 | QMAKE_DOCS = $$PWD/config/qtota.qdocconf
8 | }
9 |
10 | QMAKE_DOCS_OUTPUTDIR = $$OUT_PWD/qtota
11 |
--------------------------------------------------------------------------------
/doc/external-resources.qdoc:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the documentation of the Qt OTA.
7 | **
8 | ** $QT_BEGIN_LICENSE:FDL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use
11 | ** this file in accordance with the commercial license agreement provided
12 | ** with the Software or, alternatively, in accordance with the terms
13 | ** contained in a written agreement between you and The Qt Company. For
14 | ** licensing terms and conditions see https://www.qt.io/terms-conditions.
15 | ** For further information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU Free Documentation License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU Free
19 | ** Documentation License version 1.3 as published by the Free Software
20 | ** Foundation and appearing in the file included in the packaging of
21 | ** this file. Please review the following information to ensure
22 | ** the GNU Free Documentation License version 1.3 requirements
23 | ** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
24 | ** $QT_END_LICENSE$
25 | **
26 | ****************************************************************************/
27 |
28 | /*!
29 | \externalpage https://doc.qt.io/QtForDeviceCreation/index.html
30 | \title Qt for Device Creation
31 | */
32 |
33 | /*!
34 | \externalpage https://doc.qt.io/QtAutomotiveSuite/index.html
35 | \title Qt Automotive Suite
36 | */
37 |
--------------------------------------------------------------------------------
/doc/ota-module.qdoc:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the documentation of the Qt OTA.
7 | **
8 | ** $QT_BEGIN_LICENSE:FDL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use
11 | ** this file in accordance with the commercial license agreement provided
12 | ** with the Software or, alternatively, in accordance with the terms
13 | ** contained in a written agreement between you and The Qt Company. For
14 | ** licensing terms and conditions see https://www.qt.io/terms-conditions.
15 | ** For further information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU Free Documentation License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU Free
19 | ** Documentation License version 1.3 as published by the Free Software
20 | ** Foundation and appearing in the file included in the packaging of
21 | ** this file. Please review the following information to ensure
22 | ** the GNU Free Documentation License version 1.3 requirements
23 | ** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
24 | ** $QT_END_LICENSE$
25 | **
26 | ****************************************************************************/
27 |
28 | /*!
29 | \module qtotaupdate
30 | \qtvariable qtotaupdate
31 | \title Qt OTA Update C++ Classes
32 | */
33 |
34 | /*!
35 | \qmlmodule QtOtaUpdate 1.0
36 | \title Qt OTA Update QML Types
37 | */
38 |
39 |
--------------------------------------------------------------------------------
/doc/ota.qdoc:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | /*!
30 | \page qtota-index.html
31 | \title Over-The-Air Update
32 |
33 | Over-The-Air (OTA) update is a mechanism of distributing software updates
34 | over a wireless network without requiring physical access to a device.
35 | The target device needs to have a support for the OTA to be able to update
36 | wirelessly.
37 |
38 | The \l {http://code.qt.io/cgit/qt/qtotaupdate.git/} {Qt OTA Update} module provides tools
39 | that assist in enabling OTA update functionality in an embedded linux images build with
40 | \l {http://code.qt.io/cgit/yocto/meta-boot2qt.git/} {meta-boot2qt}. Generating new updates
41 | for OTA enabled devices is completely automated, given an ordinary linux sysroot as an input.
42 | This includes OTA updates for linux kernel, system libraries, user space applications,
43 | translation fixes, anything that is part of the sysroot. The offering includes
44 | \l {Qt OTA Update C++ Classes} {C++} and \l {Qt OTA Update QML Types} {QML} APIs to make
45 | integration with your Qt-based application a breeze.
46 |
47 | The OTA solution is based on \l {https://ostree.readthedocs.org/en/latest/} {OSTree}. If you
48 | would like to learn more about OSTree workings refer to the OSTree Documentation. There you
49 | can read about the anatomy of an OSTree repository and the deployment system, booting, and
50 | other internals of the project, as well as how OSTree compares to other update solutions.
51 |
52 | The following blog post series contain additional details on the Qt OTA Update module:
53 |
54 | \l {https://blog.qt.io/blog/2016/05/31/over-the-air-updates-part-1-introduction/}
55 | {Over-the-Air Updates, Part 1: Introduction}\br
56 | \l {https://blog.qt.io/blog/2016/06/28/over-the-air-updates-part-2-device-integration-api-and-creating-updates/}
57 | {Over-the-Air Updates, Part 2: Device Integration, API and Creating Updates}\br
58 | \l {https://blog.qt.io/blog/2016/11/11/air-updates-part-3-repository-configuration-handling/}
59 | {Over-the-Air Updates, Part 3: Repository Configuration and Handling}
60 |
61 | \section1 Features of the Update System
62 |
63 | \list
64 | \li Atomic Upgrades (all or nothing) - if an update did not fully complete,
65 | for example due to a power failure, the system will boot into an unmodified
66 | tree. The currently running tree is never modified, the update will become
67 | active on a system reboot.
68 | \li Secure - GPG signing and pinned TLS with client and server side authentication
69 | support.
70 | \li Efficient Handling of Disk Space - see the \c {/ostree} and the \c {/var}
71 | in \l {Layout of an OTA Enabled Sysroot}.
72 | \li Snapshot-based - traditional package managers (dpkg/rpm) build filesystem
73 | trees on the client side. In contrast, the primary focus of OSTree is on
74 | \e {replicating trees} composed on a server.
75 | \li Bandwidth Optimized - only the new files and the files that have changed are
76 | downloaded. When resuming from an interrupted download, only the missing files
77 | are fetched.
78 | \li Configuration Management - see the \c {/etc} in \l {Layout of an OTA
79 | Enabled Sysroot}.
80 | \li Rollback Support - atomically rollback to the previous version (tree) if
81 | something goes wrong.
82 | \li Updates Processing in Background - no unnecessary downtime for a user.
83 | \li OS updates via OTA, with support for agnostic application delivery mechanism on top.
84 | \endlist
85 |
86 | \section1 Requirements
87 |
88 | \list 1
89 |
90 | \li Filesystem.
91 |
92 | OSTree operates in userspace, and will work on top of any Linux filesystem
93 | that supports hard and symbolic links. For OSTree to function reliably, the
94 | filesystem needs to be able to recover to a consistent state after an unclean
95 | shutdown. Any journaling or log-structured filesystem, when configured properly,
96 | is capable of such recovery.
97 |
98 | \li Boot Loader.
99 |
100 | Supported boot loaders are: U-Boot, GRUB 2. \br
101 |
102 | \endlist
103 |
104 | \section1 Quick Start Guide
105 |
106 | This guide will lead you through the full workflow of how to use the provided
107 | OTA tools.
108 | \list
109 | \li Adding the OTA capability to a device before shipping it to a customer.
110 | \li Generating an update from the new version of your product's sysroot.
111 | \li Delivering this update to a customer device via OTA.
112 | \li Securing a delivery of an update.
113 | \li Support for custom update delivery mechanisms.
114 | \endlist
115 |
116 | \section2 Installation
117 |
118 | OTA package is distributed with \l {Qt for Device Creation}. The OTA-related files are
119 | installed under \c Tools/ota directory in the main SDK install location, referred to as
120 | \c SDK_INSTALL_DIR in this guide.
121 |
122 | When executing scripts, we will refer to the current working directory as
123 | WORKDIR. We will be using the \c qt-ostree tool from the installation.
124 | To see a full list of available command line arguments run the following
125 | command:
126 |
127 | \badcode
128 | ./qt-ostree --help
129 | \endcode
130 |
131 | Instead of providing a full path to \c qt-ostree each time we refer to it in
132 | the guide, we will assume to be already in the
133 | \c SDK_INSTALL_DIR/Tools/ota/qt-ostree directory.
134 |
135 | \section2 Work on Your Product
136 |
137 | Build your product on top of the \B2Q stack, or build your own custom embedded
138 | linux image. When the image is ready for the \e {first release}, continue to the
139 | \l {Enabling OTA Functionality on a Device}. Your product should also pre-integrate
140 | an updater based on the Qt OTA Update module. You can find demo updater applications
141 | in the \c SDK_INSTALL_DIR/Tools/ota/examples/ directory. Alternatively, you can
142 | install an updater at a later point as a non-system application.
143 |
144 | \section2 Enabling OTA Functionality on a Device
145 |
146 | When preparing a device for shipping and subsequent updates are to be delivered
147 | via OTA, you first need to enable this feature in the sysroot:
148 |
149 | \list 1
150 |
151 | \li Generate OSTree boot compatible initramfs image (skip this step if not using
152 | initramfs for booting).
153 |
154 | The device should be powered on, booted into your current product (the sysroot to be
155 | released), and connected to a machine from which you will run the \c generate-initramfs
156 | tool. The \l {https://en.wikipedia.org/wiki/Dracut_(software)} {Dracut} framework is
157 | used to generate the initramfs image based on the currently running kernel. The image
158 | uses systemd as an init system. You can, of course, provide your own (not necessarily
159 | Dracut-based) initramfs, as long as you include the required \l {The Booting Process}
160 | {OSTree boot logic}.
161 |
162 | To generate the initramfs image, run:
163 |
164 | \badcode
165 | cd SDK_INSTALL_DIR/Tools/ota/dracut/
166 | ./generate-initramfs
167 | \endcode
168 |
169 | This will produce an \c initramfs-${device}-${release} file in the
170 | working directory. The generated initramfs file will be needed in the later steps.
171 |
172 | \target Boot loader integration.
173 | \li Boot loader integration.
174 |
175 | OSTree maintains bootloader-independent drop-in configuration files in a format
176 | as defined by \l {https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/}
177 | {The Boot Loader Specification}. Not all boot loaders support The Boot Loader
178 | Specification, so OSTree contains code to generate native configuration files
179 | from the bootloader-independent configurations.
180 |
181 | The boot script used by your device has to be changed to use the configurations that are
182 | managed by OSTree. This will ensure that, after OTA updates or rollbacks, the correct
183 | kernel version (and corresponding boot files) will be selected at boot time.
184 |
185 | \list
186 | \li \b {U-Boot}
187 |
188 | U-Boot tools package is required. In Ubuntu, this can be installed with the following
189 | command:
190 |
191 | \badcode
192 | sudo apt-get install u-boot-tools
193 | \endcode
194 |
195 | OSTree maintains the \c uEnv.txt file, which the U-Boot environment should
196 | import. If custom changes to \c uEnv.txt are required, use the \c --uboot-env-file
197 | argument from the \c {qt-ostree} tool. The provided file will be appended to OSTree's
198 | managed \c uEnv.txt.
199 |
200 | OSTree maintains the following fields in \c uEnv.txt:
201 |
202 | \list
203 | \li \c ${kernel_image}: Path to the Linux kernel image.
204 | \li \c ${ramdisk_image}: Path to the initramfs image (optional).
205 | \li \c ${bootargs}: Parameters passed to the kernel command line.
206 | \li \c ${bootdir}: Path to other files in the \c {\boot} directory
207 | that belong to the same release and should be accessible
208 | from U-Boot (DTBs, boot scripts).
209 | \endlist
210 |
211 | An example \c uEnv.txt when booting with initramfs:
212 |
213 | \badcode
214 | kernel_image=/ostree/qt-os-590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/vmlinuz
215 | bootdir=/ostree/qt-os-590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/
216 | ramdisk_image=/ostree/qt-os-590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/initramfs
217 | bootargs=ostree=/ostree/boot.1/qt-os/590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/0
218 | \endcode
219 |
220 | A sample U-Boot logic that uses the imported OSTree's environment variables:
221 |
222 | \raw HTML
223 |
224 | if ${fs}load ${dtype} ${disk}:${part} ${script} uEnv.txt ; then
225 | env import -t ${script} ${filesize}
226 | else
227 | echo "Error loading uEnv.txt"
228 | exit
229 | fi
230 |
231 | fdt_file=<device_tree_filename>
232 |
233 | ${fs}load ${dtype} ${disk}:${part} ${kernel_addr} ${kernel_image}
234 | ${fs}load ${dtype} ${disk}:${part} ${fdt_addr} ${bootdir}/${fdt_file}
235 | ${fs}load ${dtype} ${disk}:${part} ${initramfs_addr} ${ramdisk_image}
236 |
237 | # Don't overwrite bootargs set by OSTree in uEnv.txt.
238 | setenv bootargs ${bootargs} <additional_bootargs>
239 |
240 | bootz ${kernel_addr} ${initramfs_addr} ${fdt_addr}
241 |
242 | \endraw
243 |
244 | Enabling OSTree support requires minimal effort when using a default boot script
245 | as the base. A default boot script here means whatever the device is currently using
246 | for booting. The \c {qt-ostree} tool does not change the kernel image format, only
247 | the path and the file name changes. If the original script uses the \c bootm command for
248 | loading the kernel image, then the OSTree-enabled script should use \c bootm too.
249 |
250 | \li \b {GRUB 2}
251 |
252 | Whenever the boot loader configuration files need to be updated on a GRUB 2 based system,
253 | OSTree executes \c ostree-grub-generator to convert bootloader-independent configuration
254 | files into native grub.cfg format. A default script, used by the \c qt-ostree tool is
255 | \c SDK_INSTALL_DIR/Tools/ota/qt-ostree/ostree-grub-generator.
256 | You can customize this script to match your requirements and provide it to \c qt-ostree
257 | via \c --grub2-cfg-generator. The \c ostree-grub-generator file contains additional
258 | details, the script itself is about 40 lines long.
259 |
260 | \endlist
261 |
262 | You should expect to find all the files that are required for the boot process under the
263 | \c {/boot} directory. Before starting to write the boot loader integration code, you can
264 | run the \c qt-ostree tool without providing any boot loader specific files and
265 | \l {The generated sysroot} {examine} the generated sysroot (see step 3). Particularly,
266 | inspect what gets installed in the \c {/boot} directory, as this location is of special
267 | interest to the boot loader integration code. The \c {/boot} directory may contain symbolic
268 | links to files in the \c {loader/} directory (for example, \c {uEnv.txt -> loader/uEnv.txt}).
269 | It is safe to read these symbolic links, as OSTree will ensure that the link target
270 | changes atomically on system updates and rollbacks.
271 |
272 | For more examples refer to \l {Device Integration Examples}.
273 |
274 | \target The Booting Process
275 | \b {The Booting Process}
276 |
277 | OSTree includes a special \c ostree= kernel argument that points to the corresponding tree
278 | (see the \c {/ostree} in \l {Layout of an OTA Enabled Sysroot}). When not using initramfs,
279 | the kernel command will also contain the \c init= argument, pointing to the \c ostree-prepare-root
280 | binary. The same binary is used from initramfs context. The \c ostree-prepare-root binary
281 | parses the \c ostree= kernel command line argument to find the correct versioned tree. It
282 | sets up the necessary mounts, notably the read-only mount on the \c {/usr} path, and makes
283 | the versioned tree to appear as a real \c {"/"} root directory in the booted system.
284 |
285 | After \c ostree-prepare-root (run as PID 1) completes, it passes control to the real init
286 | process. In initramfs context, once \c ostree-prepare-root is done, systemd's
287 | \c initrd-switch-root.target will take over. In initramfs, \c ostree-prepare-root is
288 | used as a user space utility (as opposed to PID 1, when booting without initramfs).
289 |
290 | \li Convert your sysroot into an OTA enabled sysroot.
291 |
292 | The conversion is done using the \c qt-ostree tool.
293 |
294 | \badcode
295 | sudo ./qt-ostree \
296 | --sysroot-image-path ${PATH_TO_SYSROOT} \
297 | --create-ota-sysroot \
298 | --ota-json ${OTA_METADATA} \
299 | --initramfs ../dracut/initramfs-${device}-${release} \
300 | --uboot-env-file ../examples/device-integration/nitrogen6x/6x_bootscript
301 | \endcode
302 |
303 | \target {The generated sysroot}
304 | The generated sysroot can be examined by mounting the \c {boot.${BOOTFS_TYPE}} and
305 | the \c {rootfs.${ROOTFS_TYPE}} filesystem images found in \c {WORKDIR}.
306 |
307 | In this guide we assume that the system is based on U-Boot boot loader.
308 |
309 | Notes on the arguments passed to \c {qt-ostree}:
310 |
311 | \list
312 | \li \b {\c --sysroot-image-path}
313 | \list
314 | \li A path to your sysroot. Binary image (\c {*.img})
315 | and archive image (\c {*.tar.gz}) is accepted as
316 | well as a path to an extracted sysroot.
317 | \endlist
318 |
319 | \li \b {\c --create-ota-sysroot}
320 | \list
321 | \li This option tells \c qt-ostree to create a binary image
322 | that contains a bootable OTA enabled sysroot. You will have to
323 | deploy the generated image to a device; in this guide, we use
324 | an SD card as memory media (see step 4).
325 | \endlist
326 |
327 | \li \b {\c --ota-json}
328 | \list
329 | \li A JSON file containing arbitrary metadata about the system.
330 | Use OtaClient::remoteMetadata to access the entire JSON file for
331 | manual parsing.
332 | \endlist
333 |
334 | \li \b {\c --initramfs}
335 | \list
336 | \li The initramfs image that we generated in the step 1. If initramfs is
337 | not used for booting, it may be necessary to provide additional
338 | kernel command line arguments (for example, \c {--kernel-args "rootwait root=/dev/sda2"}).
339 | The kernel arguments set with \b {\c --kernel-args} are passed to
340 | the bootloader integration code. If additional kernel arguments are
341 | resolved directly from boot scripts, then \c {--kernel-args} can be omitted.
342 | \endlist
343 |
344 | \li \b {\c --uboot-env-file}
345 | \list
346 | \li A custom U-Boot boot script or \c uEnv.txt file, see \l {Boot loader
347 | integration}. This argument is optional as U-Boot environment can be
348 | stored directly on the board's persistent storage dedicated for U-boot
349 | environment, or defined when building the U-Boot binary.
350 | \endlist
351 | \endlist
352 |
353 | \li Deploy the generated OTA image to an SD card.
354 |
355 | Plug in an SD card or a reader to the development host, and use the
356 | following command to find out its device name.
357 |
358 | \badcode
359 | lsblk -d
360 | \endcode
361 |
362 | Make sure to unmount all partitions on a device.
363 |
364 | \badcode
365 | sudo umount /dev/?*
366 | \endcode
367 |
368 | And then deploy the image.
369 |
370 | \badcode
371 | sudo dd bs=4M if= of=/dev/ && sync
372 | \endcode
373 |
374 | \li Test that everything went according to the plan.
375 |
376 | Boot from the SD card and run the following command \e {from the device}:
377 |
378 | \badcode
379 | ostree admin status
380 | \endcode
381 |
382 | The output should be something similar to:
383 |
384 | \badcode
385 | * qt-os 36524faa47e33da9dbded2ff99d1df47b3734427b94c8a11e062314ed31442a7.0
386 | origin refspec: qt-os:linux/qt
387 | \endcode
388 |
389 | This indicates that the deployment was successful.
390 |
391 | \note You should also verify that application(s) are working as expected
392 | and do not write outside the \l {Layout of an OTA Enabled Sysroot}
393 | {permitted paths}.
394 |
395 | \endlist
396 |
397 | \section2 Preparing a New Update for an OTA Enabled Device
398 |
399 | When preparing a \e {new update} for a device that already has OTA enabled, the
400 | workflow is as follows:
401 |
402 | \list 1
403 |
404 | \li Work on your sysroot as you normally would. When the product is ready
405 | for a release, continue to the next step.
406 |
407 | \li Generate an update.
408 |
409 | This is done by using the \c qt-ostree tool.
410 |
411 | \badcode
412 | sudo ./qt-ostree \
413 | --sysroot-image-path ${PATH_TO_SYSROOT} \
414 | --ota-json ${OTA_METADATA} \
415 | --initramfs ../dracut/initramfs-${device}-${release} \
416 | --start-trivial-httpd
417 | \endcode
418 |
419 | The above command will create a new commit in the OSTree repository at \c
420 | {WORKDIR/ostree-repo/}, or create a new repository if one does not exist.
421 | Use the \c --ostree-repo argument to provide a custom path. This repository
422 | is the OTA update source and can be exported to a production server at any time.
423 | OSTree repositories can be served via a static HTTP server.
424 |
425 | Notes on the arguments passed to \c {qt-ostree}:
426 |
427 | \list
428 | \li \b {\c --initramfs}
429 | \list
430 | \li When doing \e {minor releases} that do not update the
431 | kernel:
432 |
433 | Use the same initramfs that you already have generated
434 | for this kernel version in the earlier steps.
435 |
436 | \li When doing a \e {major release} that updates a kernel:
437 |
438 | It is advised to regenerate initramfs for each new
439 | kernel release, so that the kernel and initramfs
440 | versions \e match.
441 | \endlist
442 |
443 | As before, if not using initramfs, it may be necessary to provide
444 | additional kernel command line arguments via \b {\c --kernel-args}.
445 |
446 | \li \b {\c --sysroot-image-path}
447 | \list
448 | \li Provide a path to the \e {new version} of your sysroot.
449 | \endlist
450 | \li \b {\c --start-trivial-httpd}
451 | \list
452 | \li Starts a simple web server which you can access on the
453 | local host at address specified in \c WORKDIR/httpd/httpd-address
454 | file. This command line argument is useful for quick testing purposes,
455 | in production with more advanced requirements (for example, TLS
456 | authentication) you will need to use a different web server solution.
457 | \endlist
458 | \endlist
459 |
460 | Updating the contents of the \c {/boot} directory is supported only for major releases -
461 | when kernel/initramfs versions change. The kernel/initramfs version is considered to
462 | change when \c bootcsum changes in the following expression:
463 |
464 | \badcode
465 | bootcsum=$(cat vmlinuz initramfs | sha256sum | cut -f 1 -d ' ')
466 | \endcode
467 |
468 | \l {The generated sysroot} {Examine} the generated sysroot to see which files are installed
469 | in this directory by \c {qt-ostree} and might need to be updated together with kernel/initramfs.
470 |
471 | \li Use Qt OTA APIs to update devices.
472 |
473 | \li Go back to step 1.
474 |
475 | \endlist
476 |
477 | \section2 Securing a Delivery of an Update
478 |
479 | OTA is a component of a system and not a security framework. It is always the final product
480 | that needs to be analyzed for security implications and requirements. The Qt OTA Update module
481 | supports the following security/authentication mechanisms:
482 |
483 | \list
484 | \li \b {GNU Privacy Guard (GPG)}
485 |
486 | GPG signing helps to ensure that the data was transmitted in-full, without
487 | damage or file corruption and that the data was sent by a trusted party. A
488 | set of trusted keys is stored as keyring files on a device. Look for \c {--gpg-*}
489 | command line arguments in the output of \c {./qt-ostree --help}.
490 |
491 | In Ubuntu, the required packages can be installed with the following command:
492 |
493 | \badcode
494 | sudo apt-get install gnupg2
495 | \endcode
496 |
497 | \li \b {Transport Layer Security (TLS)}
498 |
499 | TLS protects data from tampering and eavesdropping. TLS authentication can be used on the
500 | server side to restrict the access to the server (client authentication) and on client side
501 | to verify the identitiy of an update server (server authentication).
502 | Look for \c {--tls-*} command line arguments in the output of \c {./qt-ostree --help}.
503 | \endlist
504 |
505 | To learn more about the security topics from the above list, consult dedicated resources.
506 | For the corresponding client side API see OtaRepositoryConfig.
507 |
508 | \section2 Offline Updates and Custom Delivery Mechanisms
509 |
510 | Updating devices via OtaClient::update() requires a target device to be connected to the
511 | Internet and this mechanism is limited to HTTP(S) only (OtaRepositoryConfig::url). An
512 | alternative approach is to generate a self-contained update package. A self-contained
513 | package support can be enabled by passing \c {--create-self-contained-package} to the
514 | \c {qt-ostree} tool. This will generate a \c {WORKDIR/superblock} binary file. Generating
515 | a self-contained update package is required when:
516 | \list
517 | \li A device has no network connection and is intended to be
518 | updated via external media such as a USB drive.
519 | \li Some other protocol or proprietary mechanism is used to
520 | deliver software to a device.
521 | \endlist
522 | As all APIs in the Qt OTA Update module, applying a self-contained update package is an
523 | atomic process, and is done via OtaClient::updateOffline().
524 |
525 | \section1 Layout of an OTA Enabled Sysroot
526 |
527 | There are two directories on a device for a safe storage of local files:
528 | \c {/var} and \c {/etc}.
529 | The sysroot generated by OTA tools adds convenience symbolic links in the
530 | \c {/} root directory, including symbolic links pointing to the \c {/var}.
531 |
532 | \b {Important:}
533 | \list
534 | \li Do not create or modify files in other locations, these files will be
535 | garbage collected on the next upgrade.
536 | \li Do not directly modify the contents of the \c {/ostree} and the \c {/boot}
537 | directory. This can result in a system that fails to boot.
538 | \endlist
539 |
540 | \table
541 | \header \li Directory \li Description
542 |
543 | \row \li \c {/usr}\unicode {0xA0}[read\unicode {0x2011}only]
544 |
545 | \li Everything that is part of the OS - mounted read-only to prevent
546 | inadvertent corruption. It's recommended that an operating system
547 | ships all of its content in /usr. The contents of this directory are
548 | updated via OTA.
549 |
550 | \row \li \c {/etc}
551 |
552 | \li Host-specific system-wide configuration files. OTA \e preserves all
553 | local changes by doing a 3-way merge. \br \br
554 |
555 | \b {How a 3-way merge works:}
556 |
557 | First OSTree checks on the \e {currently booted tree} which configuration
558 | files have been changed, removed or added by a user by comparing the
559 | /etc with the read-only /usr/etc/. The /usr/etc is where OSTree stores
560 | default configurations of the tree as composed on the server (each
561 | tree has its own read-only copy of the /usr/etc). The advantage of
562 | having read-only /usr/etc is that you always have access to system
563 | defaults.
564 |
565 | Then OSTree takes /etc of the \e {OTA update}, which is a separate
566 | copy from your running /etc (each tree has its own writable copy of
567 | the /etc) as a base and applies your local changes on top. It doesn’t
568 | attempt to understand the contents of files – if a configuration file
569 | on a device has been modified in any way, that wins.
570 | \row \li \c {/var}
571 |
572 | \li The only directory that is \e preserved and \e shared across upgrades.
573 | This is where user and application data should be stored.
574 | \note OSTree does not update the contents of /var, it is the
575 | responsibility of the OS to manage and upgrade /var if required.
576 |
577 | \row \li \c /ostree
578 |
579 | \li The location of the OSTree repository on a device and where the
580 | bootable versioned filesystem trees are installed. These trees \e
581 | share all common files via hard links into the OSTree repository.
582 | This means each version is deduplicated; an upgrade process only
583 | costs disk space proportional to the new files, plus some constant
584 | overhead.
585 | \note /ostree is a symbolic link to /sysroot/ostree.
586 |
587 | \row \li \c /boot
588 |
589 | \li Contains the boot loader configuration files, kernel, initramfs, and
590 | other files that are required for the boot process.
591 |
592 | \row \li \c /sysroot
593 |
594 | \li Physical / root directory mount point.
595 |
596 | \row \li \c /
597 |
598 | \li Versioned filesystem tree's mount point.
599 |
600 | \endtable
601 |
602 | \section1 Device Integration Examples
603 |
604 | Find examples for real embedded devices in the \c
605 | SDK_INSTALL_DIR/Tools/ota/examples/device-integration/ directory.
606 | */
607 |
--------------------------------------------------------------------------------
/dracut/generate-initramfs:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #############################################################################
3 | ##
4 | ## Copyright (C) 2016 The Qt Company Ltd.
5 | ## Contact: https://www.qt.io/licensing/
6 | ##
7 | ## This file is part of the Qt OTA Update module of the Qt Toolkit.
8 | ##
9 | ## $QT_BEGIN_LICENSE:GPL$
10 | ## Commercial License Usage
11 | ## Licensees holding valid commercial Qt licenses may use this file in
12 | ## accordance with the commercial license agreement provided with the
13 | ## Software or, alternatively, in accordance with the terms contained in
14 | ## a written agreement between you and The Qt Company. For licensing terms
15 | ## and conditions see https://www.qt.io/terms-conditions. For further
16 | ## information use the contact form at https://www.qt.io/contact-us.
17 | ##
18 | ## GNU General Public License Usage
19 | ## Alternatively, this file may be used under the terms of the GNU
20 | ## General Public License version 3 or (at your option) any later version
21 | ## approved by the KDE Free Qt Foundation. The licenses are as published by
22 | ## the Free Software Foundation and appearing in the file LICENSE.GPL3
23 | ## included in the packaging of this file. Please review the following
24 | ## information to ensure the GNU General Public License requirements will
25 | ## be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 | ##
27 | ## $QT_END_LICENSE$
28 | ##
29 | #############################################################################
30 | set -e
31 |
32 | ROOT=$(dirname $(readlink -f $0))
33 | ADB=$(which adb) || true
34 |
35 | if [ ! -x "${ADB}" ] ; then
36 | # No system adb found. Try the one from Qt SDK.
37 | ADB="${ROOT}"/../../../b2qt/adb
38 | if [ ! -x "${ADB}" ] ; then
39 | echo "Needed command 'adb' not found in PATH."
40 | exit 1
41 | fi
42 | fi
43 | ADB=$(readlink -e "${ADB}")
44 | echo "Using adb from ${ADB}"
45 |
46 | BOOTLOADER="u-boot"
47 |
48 | detect_target_device()
49 | {
50 | "${ADB}" pull /etc/hostname
51 | DEVICE=$(cat hostname)
52 | rm -f hostname
53 | if [ -z "${DEVICE}" ] ; then
54 | echo "error: no hostname specified in /etc/hostname on a device."
55 | exit 1
56 | fi
57 |
58 | case "${DEVICE}" in
59 | *intel-corei7*|*nuc*) # Intel NUC
60 | BOOTLOADER="grub2"
61 | ;;
62 | esac
63 |
64 | echo "Detected ${DEVICE} device with ${BOOTLOADER} boot loader."
65 | }
66 |
67 | generate_initramfs()
68 | {
69 | MODULE_PATH=/usr/lib/dracut/modules.d/
70 |
71 | init_system=$(basename $("${ADB}" shell readlink -f /sbin/init) | tr -d '\r')
72 | if [ "$init_system" != "systemd" ] ; then
73 | echo "error: Failed to detected systemd init support on the image"
74 | exit 1
75 | fi
76 |
77 | options='/boot/initramfs.img
78 | --host-only
79 | --add ostree
80 | --omit i18n
81 | --stdlog 3
82 | --force'
83 |
84 | # OSTree ships with a dracut module for systemd based images.
85 | echo "Generating initramfs for systemd based image ..."
86 | "${ADB}" push "${ROOT}"/systemd/01-qt.conf /etc/dracut.conf.d/
87 | "${ADB}" push "${ROOT}"/systemd/e2fsck.conf /etc/
88 | custom_options="--add systemd --install /etc/e2fsck.conf"
89 |
90 | # Recovery module parts
91 | #"${ADB}" push "${ROOT}"/recovery/ "${MODULE_PATH}"/98recovery/
92 | #custom_options="${custom_options} --add recovery"
93 |
94 | # Terminate when the explicitly required modules could not be found or installed.
95 | "${ADB}" shell dracut ${options} ${custom_options} | tee dracut.log
96 | errors=$(cat dracut.log | grep -i "cannot be found or installed" | wc -l)
97 | if [ ${errors} -gt 0 ] ; then
98 | echo "error: Failed to include the required modules into the initramfs image."
99 | exit 1
100 | fi
101 |
102 | rm -f initramfs.img
103 | "${ADB}" pull /boot/initramfs.img
104 | device=$("${ADB}" shell uname -n | tr -d '\r')
105 | release=$("${ADB}" shell uname -r | tr -d '\r' | cut -d'-' -f1)
106 | INITRAMFS=initramfs-${device}-${release}
107 | rm -rf ${INITRAMFS}
108 |
109 | detect_target_device
110 |
111 | if [ "${BOOTLOADER}" = "u-boot" ] ; then
112 | # Add u-boot header.
113 | SOURCE_DATE_EPOCH=1 mkimage -A arm -O linux -T ramdisk -a 0 -e 0 -d initramfs.img ${INITRAMFS}
114 | rm -f initramfs.img
115 | else
116 | mv initramfs.img ${INITRAMFS}
117 | fi
118 | }
119 |
120 | main()
121 | {
122 | generate_initramfs
123 |
124 | echo
125 | echo "Done, generated OSTree boot compatible initramfs:"
126 | echo
127 | echo ${INITRAMFS}
128 | echo
129 | }
130 |
131 | main
132 |
--------------------------------------------------------------------------------
/dracut/recovery/module-setup.sh:
--------------------------------------------------------------------------------
1 | #############################################################################
2 | ##
3 | ## Copyright (C) 2016 The Qt Company Ltd.
4 | ## Contact: http://www.qt.io/licensing/
5 | ##
6 | ## This file is part of the Qt Enterprise Embedded Scripts of the Qt
7 | ## framework.
8 | ##
9 | ## $QT_BEGIN_LICENSE:COMM$
10 | ##
11 | ## Commercial License Usage
12 | ## Licensees holding valid commercial Qt licenses may use this file in
13 | ## accordance with the commercial license agreement provided with the
14 | ## Software or, alternatively, in accordance with the terms contained in
15 | ## a written agreement between you and The Qt Company. For licensing terms
16 | ## and conditions see http://www.qt.io/terms-conditions. For further
17 | ## information use the contact form at http://www.qt.io/contact-us.
18 | ##
19 | ## $QT_END_LICENSE$
20 | ##
21 | #############################################################################
22 |
23 | check() {
24 | return 255
25 | }
26 |
27 | depends() {
28 | return 0
29 | }
30 |
31 | install() {
32 | inst_hook pre-mount 10 "$moddir/recovery.sh"
33 | }
34 |
--------------------------------------------------------------------------------
/dracut/recovery/recovery.sh:
--------------------------------------------------------------------------------
1 | #############################################################################
2 | ##
3 | ## Copyright (C) 2016 The Qt Company Ltd.
4 | ## Contact: http://www.qt.io/licensing/
5 | ##
6 | ## This file is part of the Qt Enterprise Embedded Scripts of the Qt
7 | ## framework.
8 | ##
9 | ## $QT_BEGIN_LICENSE:COMM$
10 | ##
11 | ## Commercial License Usage
12 | ## Licensees holding valid commercial Qt licenses may use this file in
13 | ## accordance with the commercial license agreement provided with the
14 | ## Software or, alternatively, in accordance with the terms contained in
15 | ## a written agreement between you and The Qt Company. For licensing terms
16 | ## and conditions see http://www.qt.io/terms-conditions. For further
17 | ## information use the contact form at http://www.qt.io/contact-us.
18 | ##
19 | ## $QT_END_LICENSE$
20 | ##
21 | #############################################################################
22 |
23 | . /lib/dracut-lib.sh
24 |
25 | if $(getargbool recovery) ; then
26 | warn "Doing actual recovery"
27 | root_partition=$(getarg root)
28 | boot_partition=${root_partition::-1}$((${root_partition: -1} -1))
29 |
30 | e2fsck -p "${boot_partition}"
31 | e2fsck -p "${root_partition}"
32 | # 'reboot' and 'systemctl reboot' are shutting down the system but do not
33 | # trigger a reset after halting.
34 | # WARNING: Make sure to sync and unmount all mounted disks before restarting in this way.
35 | echo b >/proc/sysrq-trigger
36 | else
37 | warn "Not doing recovery"
38 | fi
39 |
--------------------------------------------------------------------------------
/dracut/systemd/01-qt.conf:
--------------------------------------------------------------------------------
1 | # Dracut config file customized for meta-b2qt based images.
2 | udevdir="/lib/udev/"
3 | systemdutildir="/lib/systemd/"
4 | systemdsystemunitdir="/lib/systemd/system/"
5 |
--------------------------------------------------------------------------------
/dracut/systemd/e2fsck.conf:
--------------------------------------------------------------------------------
1 | [options]
2 | broken_system_clock = true
3 |
4 |
--------------------------------------------------------------------------------
/examples/cpp/basic-daemon/basic-daemon.pro:
--------------------------------------------------------------------------------
1 | QT += core qtotaupdate
2 |
3 | TARGET = basic-daemon
4 | TEMPLATE = app
5 |
6 | SOURCES += main.cpp
7 |
--------------------------------------------------------------------------------
/examples/cpp/basic-daemon/main.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | Q_LOGGING_CATEGORY(daemon, "ota.daemon.demo", QtDebugMsg)
38 |
39 | class UpdateChecker : public QObject
40 | {
41 | Q_OBJECT
42 | public:
43 | UpdateChecker(const QString &guiUpdater) :
44 | m_device(&QOtaClient::instance()),
45 | m_guiUpdaterPath(guiUpdater)
46 | {
47 | connect(m_device, &QOtaClient::fetchRemoteMetadataFinished, this, &UpdateChecker::fetchFinished);
48 | connect(m_device, &QOtaClient::statusStringChanged, this, &UpdateChecker::log);
49 | connect(m_device, &QOtaClient::errorOccurred, this, &UpdateChecker::logError);
50 | connect(&m_fetchTimer, &QTimer::timeout, this, &UpdateChecker::startFetch);
51 |
52 | m_repoConfig.setUrl(QStringLiteral("http://www.b2qtupdate.com/ostree-repo"));
53 | if (!m_device->isRepositoryConfigSet(&m_repoConfig))
54 | m_device->setRepositoryConfig(&m_repoConfig);
55 |
56 | m_fetchTimer.setSingleShot(true);
57 | m_fetchTimer.start();
58 | }
59 |
60 | void log(const QString &message) const { qCInfo(daemon) << message; }
61 | void logError(const QString &error) const {
62 | qCInfo(daemon) << QString(error).prepend(QStringLiteral("error: "));
63 | }
64 |
65 | void startFetch()
66 | {
67 | log(QStringLiteral("verifying remote server for system updates..."));
68 | m_device->fetchRemoteMetadata();
69 | }
70 |
71 | void fetchFinished(bool success)
72 | {
73 | if (success && m_device->updateAvailable()) {
74 | log(QStringLiteral("update available"));
75 | // Any inter-process communication mechanism could be used here. In this demo we
76 | // simply launch a GUI that can be used to execute the update commands (such as examples/qml/basic/).
77 | // A more sophisticated approach would be to use IPC (such as a push notification) to let the
78 | // already running UI know that there is an system update available. Then this UI can open
79 | // OTA control view or call OtaClient::refreshMetadata() if it is already at the OTA control view.
80 | QString cmd = QString(m_guiUpdaterPath).prepend(QStringLiteral("/usr/bin/appcontroller "));
81 | log(QString(cmd).prepend(QStringLiteral("starting GUI: ")));
82 | bool ok = QProcess::startDetached(cmd);
83 | if (!ok)
84 | logError(QString(cmd).prepend(QStringLiteral("failed to start updater GUI: ")));
85 | // Here we assume that the system restarts the daemon on the next reboot. Alternatively
86 | // GUI could use IPC to give further instructions to the daemon (based on users actions).
87 | qApp->quit();
88 | } else {
89 | log(QStringLiteral("no updates"));
90 | // Check again 2 seconds later.
91 | m_fetchTimer.start(2000);
92 | }
93 | }
94 |
95 | private:
96 | QOtaClient *m_device;
97 | QOtaRepositoryConfig m_repoConfig;
98 | QTimer m_fetchTimer;
99 | QString m_guiUpdaterPath;
100 | };
101 |
102 | #include "main.moc"
103 |
104 | int main(int argc, char *argv[])
105 | {
106 | QCoreApplication app(argc, argv);
107 |
108 | QCommandLineParser parser;
109 | parser.addHelpOption();
110 | parser.setApplicationDescription(QStringLiteral("Qt OTA Update daemon demo."));
111 | QCommandLineOption verboseOption(QStringLiteral("v"),
112 | QStringLiteral("Print verbose debug messages from the Qt OTA Update library."));
113 | parser.addOption(verboseOption);
114 | QCommandLineOption guiUpdaterOption(QStringLiteral("gui-path"),
115 | QStringLiteral("A path to the GUI updater to start when a new system update is detected."),
116 | QStringLiteral("Path"));
117 | parser.addOption(guiUpdaterOption);
118 |
119 | parser.process(app);
120 |
121 | if (parser.isSet(verboseOption))
122 | QLoggingCategory::setFilterRules(QStringLiteral("b2qt.ota.debug=true"));
123 | QString guiAppPath = parser.value(guiUpdaterOption);
124 | if (guiAppPath.isEmpty()) {
125 | qWarning() << "--gui-path is required.";
126 | return EXIT_FAILURE;
127 | }
128 |
129 | UpdateChecker checker(guiAppPath);
130 |
131 | return app.exec();
132 | }
133 |
--------------------------------------------------------------------------------
/examples/device-integration/README:
--------------------------------------------------------------------------------
1 | SUMMARY
2 |
3 | This directory contains OTA device integration examples.
4 |
5 | The source code has been fetched from each board's corresponding yocto meta
6 | layers.
7 |
8 | EXAMPLE 1 - nitrogen6x/ (Boundary Devices i.MX6)
9 |
10 | On Boundary Devices boards default 'bootcmd' contains:
11 |
12 | bootcmd =
13 | for dtype in ${bootdevs}; do
14 | if itest.s "xusb" == "x${dtype}" ; then
15 | usb start ;
16 | fi;
17 | for disk in 0 1 ; do
18 | ${dtype} dev ${disk} ;
19 | for fs in fat ext2 ; do
20 | ${fs}load ${dtype} ${disk}:1 10008000 /6x_bootscript&& source 10008000 ;
21 | done ;
22 | done ;
23 | done;
24 | setenv stdout serial,vga ; echo ;
25 | echo 6x_bootscript not found ; echo ;
26 | echo serial console at 115200, 8N1 ; echo ;
27 | echo details at http://boundarydevices.com/6q_bootscript ;
28 | setenv stdin serial,usbkbd
29 |
30 | From 'bootcmd' command we see that an additional boot script is imported -
31 | 6x_bootscript. By examining the 6x_bootscript we see that a board by default
32 | imports uEnv.txt file, if it exists. Therefore, to enable OSTree on the board
33 | we can modify default boot script in the following way:
34 |
35 | Note: To work with the 6x_bootscript you can simply make a copy of it, open
36 | the copy with 'vim' editor and manually remove the u-boot header.
37 |
38 | Replace:
39 | if ${fs}load ${dtype} ${disk}:1 10800000 ${bootdir}uImage ; then
40 | if itest.s x$havedtb == x ; then
41 | bootm 10800000 ;
42 | else
43 | bootm 10800000 - 12000000
44 | fi
45 | fi
46 | With:
47 | # Use kernel_image and ramdisk_image values that were imported from the
48 | # uEnv.txt, generated by OSTree.
49 | ${fs}load ${dtype} ${disk}:1 10800000 ${kernel_image}
50 | setenv ramdisk_addr 0x40000000
51 | ${fs}load ${dtype} ${disk}:1 ${ramdisk_addr} ${ramdisk_image}
52 |
53 | bootm 10800000 ${ramdisk_addr} 12000000
54 |
55 | EXAMPLE 2 - beaglebone/ (BeagleBone Black)
56 |
57 | Tested on a device with u-boot version U-Boot 2013.04-dirty (Jun 19 2013 - 09:57:14).
58 |
59 | On beaglebone default 'bootcmd' contains:
60 |
61 | bootcmd=
62 | gpio set 53;
63 | i2c mw 0x24 1 0x3e;
64 | run findfdt;
65 | mmc dev 0;
66 |
67 | if mmc rescan ; then
68 | echo micro SD card found;
69 | setenv mmcdev 0;
70 | else
71 | echo No micro SD card found, setting mmcdev to 1;
72 | setenv mmcdev 1;
73 | fi;
74 |
75 | setenv bootpart ${mmcdev}:2;
76 | mmc dev ${mmcdev};
77 |
78 | if mmc rescan; then
79 | gpio set 54;
80 | echo SD/MMC found on device ${mmcdev};
81 | if run loadbootenv; then
82 | echo Loaded environment from ${bootenv};
83 | run importbootenv;
84 | fi;
85 |
86 | if test -n $uenvcmd; then
87 | echo Running uenvcmd ...;
88 | run uenvcmd;
89 | fi;
90 |
91 | gpio set 55;
92 |
93 | if run loaduimage; then
94 | gpio set 56;
95 | run loadfdt;
96 | run mmcboot;
97 | fi;
98 | fi;
99 |
100 | From 'bootcmd' command we see that beaglebone uses 'loadbootenv' to import
101 | an additional environment. To see what 'loadbootenv' command contains, execute
102 | the following from u-boot console:
103 |
104 | printenv loadbootenv
105 | loadbootenv=load mmc ${mmcdev} ${loadaddr} ${bootenv}
106 |
107 | printenv bootenv
108 | bootenv=uEnv.txt
109 |
110 | Now we know that it sources uEnv.txt. To enable OSTree on this board we will
111 | add our custom commands in the uEnv.txt file.
112 |
113 | Default uEnv.txt contains:
114 |
115 | optargs=consoleblank=0 vt.global_cursor_default=0 quiet
116 | # extra options to support older u-boot (2013)
117 | bootfile=zImage
118 | loadaddr=0x80200000
119 | loaduimage=load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile}
120 | mmcboot=echo Booting from mmc ...; run mmcargs; bootz ${loadaddr} - ${fdtaddr}
121 |
122 | After our changes it contains:
123 |
124 | # OSTree will insert its environment on top of uEnv.txt file.
125 | bootpart=0:1
126 | optargs=consoleblank=0 vt.global_cursor_default=0 quiet
127 | mmcroot=/dev/mmcblk0p2
128 | loaduimage=load mmc ${bootpart} ${loadaddr} ${kernel_image}
129 | loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}${fdtfile}
130 | loadramdisk=load mmc ${bootpart} ${rdaddr} ${ramdisk_image}
131 | mmcargs=setenv bootargs $bootargs console=${console} ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype}
132 | mmcboot=run loadramdisk; echo Booting from mmc ....; run mmcargs; bootz ${loadaddr} ${rdaddr} ${fdtaddr}
133 |
134 | When writing custom uEnv.txt, we can assume that OSTree's environment
135 | (kernel_image and etc) is already defined. OSTree merges its environment
136 | variables at the top of uEnv.txt file. You can see an example of this when running
137 | 'qt-ostree' with the following arguments:
138 |
139 | ./qt-ostree --create-ota-sysroot \
140 | --uboot-env-file uEnv.txt \ (your custom uEnv.txt)
141 | --sysroot-image-path $SYSROOT_IMAGE_PATH \
142 | --initramfs $INITRAMFS
143 |
144 | Examine the generated $WORKDIR/sysroot/boot/uEnv.txt file.
145 |
146 | EXAMPLE 3 - colibri-vf/ (Toradex Colibri VF)
147 |
148 | On this board default 'bootcmd' does not source any external environment.
149 | There are several options what we can do:
150 |
151 | 1) Update environment directly on the board.
152 |
153 | setenv loadbootenv 'load mmc 0 ${loadaddr} uEnv.txt'
154 | setenv importbootenv 'echo Importing environment from mmc ...; env import -t $loadaddr $filesize'
155 | setenv bootcmd 'if run loadbootenv; then echo Loaded environment from uEnv.txt; run importbootenv; fi; run sdboot; run ubiboot; run nfsboot'
156 | saveenv
157 |
158 | Where our uEnv.txt contains:
159 |
160 | # OSTree will insert its environment on top of uEnv.txt file.
161 | bootpart=0:1
162 | ramdisk_addr_r=0x86000000
163 | loaduimage=load mmc ${bootpart} ${kernel_addr_r} ${kernel_image}
164 | loadfdt=load mmc ${bootpart} ${fdt_addr_r} ${bootdir}${soc}-colibri-${fdt_board}.dtb
165 | loadramdisk=load mmc ${bootpart} ${ramdisk_addr_r} ${ramdisk_image}
166 | sdboot=run setup; setenv bootargs ${bootargs} ${defargs} ${sdargs} ${mtdparts} ${setupargs} ${vidargs}; echo Booting from MMC/SD card...; run loaduimage && run loadfdt && run loadramdisk && bootz ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
167 |
168 | 2) Update u-boot configuration for the board.
169 |
170 | This can be done by editing include/configs/colibri_vf.h in the u-boot
171 | repository.
172 |
173 | #define CONFIG_BOOTCOMMAND
174 | #define CONFIG_EXTRA_ENV_SETTINGS
175 |
176 | EXAMPLE 4 - imx6qsabresd/ (Freescale SABRE SD i.MX6)
177 |
178 | On this board default 'bootcmd' runs 'loadbootscript' to load an additional
179 | environment.
180 |
181 | loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
182 |
183 | We see that assumption is that boot files are on FAT partition (fatload). This
184 | won't work because we require a file system with symbolic link support. By
185 | default we format boot partition as ext2, so we need to change the boot script
186 | accordingly:
187 |
188 | setenv loadbootscript 'ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}'
189 | saveenv
190 |
191 | Find the name of the boot script:
192 |
193 | printenv script
194 | script=boot.scr
195 |
196 | Add OSTree support by creating boot.scr file with the following contents:
197 |
198 | ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} uEnv.txt
199 | echo Importing environment from mmc ...
200 | env import -t $loadaddr $filesize
201 | setenv ramdisk_addr 0x24000000
202 | ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${kernel_image}
203 | ext2load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}${fdt_file}
204 | ext2load mmc ${mmcdev}:${mmcpart} ${ramdisk_addr} ${ramdisk_image}
205 | echo Booting from mmc ...
206 | run videoargs
207 | setenv bootargs ${bootargs} console=${console},${baudrate} video=${video} consoleblank=0 vt.global_cursor_default=0 root=${mmcroot}
208 | bootz ${loadaddr} ${ramdisk_addr} ${fdt_addr}
209 |
--------------------------------------------------------------------------------
/examples/device-integration/beaglebone/uEnv.txt:
--------------------------------------------------------------------------------
1 | bootpart=0:1
2 | optargs=consoleblank=0 vt.global_cursor_default=0 quiet
3 | mmcroot=/dev/mmcblk0p2
4 | loaduimage=load mmc ${bootpart} ${loadaddr} ${kernel_image}
5 | loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}
6 | loadramdisk=load mmc ${bootpart} ${rdaddr} ${ramdisk_image}
7 | mmcargs=setenv bootargs $bootargs console=${console} ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype}
8 | mmcboot=run loadramdisk; echo Booting from mmc ....; run mmcargs; bootz ${loadaddr} ${rdaddr} ${fdtaddr}
9 |
10 |
--------------------------------------------------------------------------------
/examples/device-integration/colibri-vf/uEnv.txt:
--------------------------------------------------------------------------------
1 | bootpart=0:1
2 | ramdisk_addr_r=0x86000000
3 | loaduimage=load mmc ${bootpart} ${kernel_addr_r} ${kernel_image}
4 | loadfdt=load mmc ${bootpart} ${fdt_addr_r} ${bootdir}/${soc}-colibri-${fdt_board}.dtb
5 | loadramdisk=load mmc ${bootpart} ${ramdisk_addr_r} ${ramdisk_image}
6 | sdboot=run setup; setenv bootargs ${bootargs} ${defargs} ${sdargs} ${mtdparts} ${setupargs} ${vidargs}; echo Booting from MMC/SD card...; run loaduimage && run loadfdt && run loadramdisk && bootz ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
7 |
--------------------------------------------------------------------------------
/examples/device-integration/imx6qsabresd/boot.scr:
--------------------------------------------------------------------------------
1 | ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} uEnv.txt
2 | echo Importing environment from mmc ...
3 | env import -t $loadaddr $filesize
4 | setenv ramdisk_addr 0x24000000
5 | ext2load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${kernel_image}
6 | ext2load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}
7 | ext2load mmc ${mmcdev}:${mmcpart} ${ramdisk_addr} ${ramdisk_image}
8 | echo Booting from mmc ...
9 | run videoargs
10 | setenv bootargs ${bootargs} console=${console},${baudrate} video=${video} consoleblank=0 vt.global_cursor_default=0 root=${mmcroot}
11 | bootz ${loadaddr} ${ramdisk_addr} ${fdt_addr}
12 |
--------------------------------------------------------------------------------
/examples/device-integration/nitrogen6x/6x_bootscript:
--------------------------------------------------------------------------------
1 | setenv bootargs ''
2 |
3 | a_script=0x10800000
4 | a_zImage=0x10800000
5 | a_fdt=0x13000000
6 | a_ramdisk=0x13800000
7 | a_initrd=0x13a00000
8 |
9 | setenv initrd_high 0xffffffff
10 | if itest.s x6SX == "x${cpu}" ; then
11 | a_script=0x80800000
12 | a_zImage=0x80800000
13 | a_fdt=0x83000000
14 | a_ramdisk=0x83800000
15 | a_initrd=0x83a00000
16 | fi
17 |
18 | if itest.s "x" == "x${dtbname}" ; then
19 | if itest.s x6SOLO == "x${cpu}" ; then
20 | dtbname=imx6dl-${board}.dtb;
21 | elif itest.s x6DL == "x${cpu}" ; then
22 | dtbname=imx6dl-${board}.dtb;
23 | elif itest.s x6SX == "x${cpu}" ; then
24 | dtbname=imx6sx-${board}.dtb;
25 | else
26 | dtbname=imx6q-${board}.dtb;
27 | fi
28 | fi
29 |
30 | if itest.s x${bootpart} == x ; then
31 | bootpart=1
32 | fi
33 | echo Booting from partition ${bootpart}
34 |
35 | if load ${dtype} ${disk}:${bootpart} ${a_script} uEnv.txt ; then
36 | env import -t ${a_script} ${filesize}
37 | fi
38 |
39 | if itest.s "x" != "x${recovery}" ; then
40 | echo "Starting up recovery mode"
41 | kernel_image=/vmlinuz
42 | ramdisk_image=/initramfs
43 | setenv bootargs ${bootargs} recovery
44 | fi
45 |
46 | if itest.s x == x${bootdir} ; then
47 | bootdir=/
48 | fi
49 |
50 | if load ${dtype} ${disk}:${bootpart} ${a_fdt} ${bootdir}${dtbname} ; then
51 | fdt addr ${a_fdt}
52 | setenv fdt_high 0xffffffff
53 | else
54 | echo "!!!! Error loading ${bootdir}${dtbname}";
55 | exit;
56 | fi
57 |
58 |
59 | # first enable tfp410
60 | i2c dev 1
61 | i2c mw 38 8.1 bd
62 |
63 |
64 | cmd_xxx_present=
65 | fdt resize
66 | if itest.s "x" != "x${cmd_custom}" ; then
67 | run cmd_custom
68 | cmd_xxx_present=1;
69 | fi
70 |
71 | if itest.s "x" != "x${cmd_hdmi}" ; then
72 | run cmd_hdmi
73 | cmd_xxx_present=1;
74 | if itest.s x == x${allow_noncea} ; then
75 | setenv bootargs ${bootargs} mxc_hdmi.only_cea=1;
76 | echo "only CEA modes allowed on HDMI port";
77 | else
78 | setenv bootargs ${bootargs} mxc_hdmi.only_cea=0;
79 | echo "non-CEA modes allowed on HDMI, audio may be affected";
80 | fi
81 | fi
82 |
83 | if itest.s "x" != "x${cmd_lcd}" ; then
84 | run cmd_lcd
85 | cmd_xxx_present=1;
86 | fi
87 | if itest.s "x" != "x${cmd_lvds}" ; then
88 | run cmd_lvds
89 | cmd_xxx_present=1;
90 | fi
91 | if itest.s "x" != "x${cmd_lvds2}" ; then
92 | run cmd_lvds2
93 | cmd_xxx_present=1;
94 | fi
95 |
96 | if itest.s "x" == "x${cmd_xxx_present}" ; then
97 | echo "!!!!!!!!!!!!!!!!"
98 | echo "warning: your u-boot may be outdated, please upgrade"
99 | echo "!!!!!!!!!!!!!!!!"
100 | fi
101 |
102 | setenv bootargs "${bootargs} console=${console},115200 vmalloc=400M cma=384M consoleblank=0 rootwait fixrtc"
103 |
104 |
105 | bpart=2
106 |
107 | if test "sata" = "${dtype}" ; then
108 | setenv bootargs "${bootargs} root=/dev/sda${bpart}" ;
109 | elif test "usb" = "${dtype}" ; then
110 | setenv bootargs "${bootargs} root=/dev/sda${bpart}" ;
111 | else
112 | setenv bootargs "${bootargs} root=/dev/mmcblk${disk}p${bpart}"
113 | fi
114 |
115 | if test "hannstar" = "${fb_lvds}" || test "hannstar" = "${fb_lvds2}" ; then
116 | setenv bootargs ${bootargs} ft5x06_ts.screenres=1024,600
117 | elif test "hannstar7" = "${fb_lvds}" || test "hannstar7" = "${fb_lvds2}" ; then
118 | setenv bootargs ${bootargs} ft5x06_ts.screenres=1280,800
119 | fi
120 |
121 | if itest.s "x" != "x${disable_giga}" ; then
122 | setenv bootargs ${bootargs} fec.disable_giga=1
123 | fi
124 |
125 | if itest.s "x" != "x${wlmac}" ; then
126 | setenv bootargs ${bootargs} wlcore.mac=${wlmac}
127 | fi
128 |
129 | if itest.s "x" != "x${gpumem}" ; then
130 | setenv bootargs ${bootargs} galcore.contiguousSize=${gpumem}
131 | fi
132 |
133 | if itest.s "x" != "x${cma}" ; then
134 | setenv bootargs ${bootargs} cma=${cma}
135 | fi
136 |
137 | if itest.s "x" != "x${show_fdt}" ; then
138 | fdt print /
139 | fi
140 |
141 | if itest.s "x" != "x${show_env}" ; then
142 | printenv
143 | fi
144 |
145 | if load ${dtype} ${disk}:${bootpart} ${a_ramdisk} ${ramdisk_image} ; then
146 | if load ${dtype} ${disk}:${bootpart} ${a_zImage} ${kernel_image} ; then
147 | bootz ${a_zImage} ${a_ramdisk} ${a_fdt}
148 | fi
149 | fi
150 | echo "Error loading kernel image"
151 |
--------------------------------------------------------------------------------
/examples/device-integration/nitrogen6x/no-initramfs/6x_bootscript:
--------------------------------------------------------------------------------
1 | setenv bootargs ''
2 |
3 | a_script=0x10800000
4 | a_zImage=0x10800000
5 | a_fdt=0x13000000
6 | a_ramdisk=0x13800000
7 | a_initrd=0x13a00000
8 |
9 | setenv initrd_high 0xffffffff
10 | if itest.s x6SX == "x${cpu}" ; then
11 | a_script=0x80800000
12 | a_zImage=0x80800000
13 | a_fdt=0x83000000
14 | a_ramdisk=0x83800000
15 | a_initrd=0x83a00000
16 | fi
17 |
18 | if itest.s "x" == "x${dtbname}" ; then
19 | if itest.s x6SOLO == "x${cpu}" ; then
20 | dtbname=imx6dl-${board}.dtb;
21 | elif itest.s x6DL == "x${cpu}" ; then
22 | dtbname=imx6dl-${board}.dtb;
23 | elif itest.s x6SX == "x${cpu}" ; then
24 | dtbname=imx6sx-${board}.dtb;
25 | else
26 | dtbname=imx6q-${board}.dtb;
27 | fi
28 | fi
29 |
30 | if itest.s x${bootpart} == x ; then
31 | bootpart=1
32 | fi
33 | echo Booting from partition ${bootpart}
34 |
35 | if load ${dtype} ${disk}:${bootpart} ${a_script} uEnv.txt ; then
36 | env import -t ${a_script} ${filesize}
37 | fi
38 |
39 | if itest.s x == x${bootdir} ; then
40 | bootdir=/
41 | fi
42 |
43 | if load ${dtype} ${disk}:${bootpart} ${a_fdt} ${bootdir}${dtbname} ; then
44 | fdt addr ${a_fdt}
45 | setenv fdt_high 0xffffffff
46 | else
47 | echo "!!!! Error loading ${bootdir}${dtbname}";
48 | exit;
49 | fi
50 |
51 |
52 | # first enable tfp410
53 | i2c dev 1
54 | i2c mw 38 8.1 bd
55 |
56 |
57 | cmd_xxx_present=
58 | fdt resize
59 | if itest.s "x" != "x${cmd_custom}" ; then
60 | run cmd_custom
61 | cmd_xxx_present=1;
62 | fi
63 |
64 | if itest.s "x" != "x${cmd_hdmi}" ; then
65 | run cmd_hdmi
66 | cmd_xxx_present=1;
67 | if itest.s x == x${allow_noncea} ; then
68 | setenv bootargs ${bootargs} mxc_hdmi.only_cea=1;
69 | echo "only CEA modes allowed on HDMI port";
70 | else
71 | setenv bootargs ${bootargs} mxc_hdmi.only_cea=0;
72 | echo "non-CEA modes allowed on HDMI, audio may be affected";
73 | fi
74 | fi
75 |
76 | if itest.s "x" != "x${cmd_lcd}" ; then
77 | run cmd_lcd
78 | cmd_xxx_present=1;
79 | fi
80 | if itest.s "x" != "x${cmd_lvds}" ; then
81 | run cmd_lvds
82 | cmd_xxx_present=1;
83 | fi
84 | if itest.s "x" != "x${cmd_lvds2}" ; then
85 | run cmd_lvds2
86 | cmd_xxx_present=1;
87 | fi
88 |
89 | if itest.s "x" == "x${cmd_xxx_present}" ; then
90 | echo "!!!!!!!!!!!!!!!!"
91 | echo "warning: your u-boot may be outdated, please upgrade"
92 | echo "!!!!!!!!!!!!!!!!"
93 | fi
94 |
95 | setenv bootargs "${bootargs} console=${console},115200 vmalloc=400M cma=384M consoleblank=0 rootwait fixrtc"
96 |
97 |
98 | bpart=2
99 |
100 | if test "sata" = "${dtype}" ; then
101 | setenv bootargs "${bootargs} root=/dev/sda${bpart}" ;
102 | elif test "usb" = "${dtype}" ; then
103 | setenv bootargs "${bootargs} root=/dev/sda${bpart}" ;
104 | else
105 | setenv bootargs "${bootargs} root=/dev/mmcblk${disk}p${bpart}"
106 | fi
107 |
108 | if test "hannstar" = "${fb_lvds}" || test "hannstar" = "${fb_lvds2}" ; then
109 | setenv bootargs ${bootargs} ft5x06_ts.screenres=1024,600
110 | elif test "hannstar7" = "${fb_lvds}" || test "hannstar7" = "${fb_lvds2}" ; then
111 | setenv bootargs ${bootargs} ft5x06_ts.screenres=1280,800
112 | fi
113 |
114 | if itest.s "x" != "x${disable_giga}" ; then
115 | setenv bootargs ${bootargs} fec.disable_giga=1
116 | fi
117 |
118 | if itest.s "x" != "x${wlmac}" ; then
119 | setenv bootargs ${bootargs} wlcore.mac=${wlmac}
120 | fi
121 |
122 | if itest.s "x" != "x${gpumem}" ; then
123 | setenv bootargs ${bootargs} galcore.contiguousSize=${gpumem}
124 | fi
125 |
126 | if itest.s "x" != "x${cma}" ; then
127 | setenv bootargs ${bootargs} cma=${cma}
128 | fi
129 |
130 | if itest.s "x" != "x${show_fdt}" ; then
131 | fdt print /
132 | fi
133 |
134 | if itest.s "x" != "x${show_env}" ; then
135 | printenv
136 | fi
137 |
138 | if load ${dtype} ${disk}:${bootpart} ${a_zImage} ${kernel_image} ; then
139 | bootz ${a_zImage} - ${a_fdt}
140 | fi
141 |
142 | echo "Error loading kernel image"
143 |
--------------------------------------------------------------------------------
/examples/qml/basic/basic.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | QT += qml quick
4 |
5 | SOURCES += main.cpp
6 |
7 | RESOURCES += qml.qrc
8 |
9 | target.path = /var/$$TARGET
10 | INSTALLS += target
11 |
--------------------------------------------------------------------------------
/examples/qml/basic/main.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #include
30 | #include
31 |
32 | int main(int argc, char *argv[])
33 | {
34 | QGuiApplication app(argc, argv);
35 |
36 | qputenv("QT_QUICK_CONTROLS_STYLE", "material");
37 |
38 | QQmlApplicationEngine engine;
39 | engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
40 |
41 | return app.exec();
42 | }
43 |
--------------------------------------------------------------------------------
/examples/qml/basic/main.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | import QtQuick 2.7
30 | import QtQuick.Window 2.2
31 | import QtQuick.Controls 2.0
32 | import QtQuick.Layouts 1.3
33 | import QtOtaUpdate 1.0
34 |
35 | Window {
36 | visible:true
37 |
38 | function log(message) { logFormatted(message, "black") }
39 | function logError(message) { logFormatted(message, "red") }
40 | function logWithCondition(message, condition) {
41 | var color = condition ? "black" : "red"
42 | var suffix = condition ? " finished" : " failed"
43 | logFormatted(message + suffix, color)
44 | }
45 | function logFormatted(message, color) {
46 | logRecords.append({
47 | "record" : "" + (logRecords.count + 1) + " " + message,
48 | "textcolor" : color
49 | })
50 | logView.positionViewAtEnd()
51 | }
52 |
53 | function otaEnabled() {
54 | if (!OtaClient.otaEnabled) {
55 | log("OTA Update functionality is not enabled on this device")
56 | return false;
57 | }
58 | return true;
59 | }
60 |
61 | function configureRepository(config, silent) {
62 | if (OtaClient.isRepositoryConfigSet(config)) {
63 | if (!silent)
64 | log("The configuration is already set")
65 | return false;
66 | } else {
67 | if (!OtaClient.removeRepositoryConfig()) {
68 | logError("Failed to remove repository configuration")
69 | return false;
70 | }
71 | }
72 | if (!OtaClient.setRepositoryConfig(config)) {
73 | logError("Failed to update repository configuration")
74 | return false;
75 | }
76 | if (!silent)
77 | log("Successfully updated repository configuration")
78 | return true;
79 | }
80 |
81 | function updateConfigView(config) {
82 | repoConfigLabel.text = "URL: " + (config ? config.url : "not set")
83 | repoConfigLabel.text += "
GPG Verify: " + (config ? config.gpgVerify : "not set")
84 | repoConfigLabel.text += "
TLS Client Cert: " + (config ? config.tlsClientCertPath : "not set")
85 | repoConfigLabel.text += "
TLS Client Key: " + (config ? config.tlsClientKeyPath : "not set")
86 | repoConfigLabel.text += "
TLS Permissive: " + (config ? config.tlsPermissive : "not set")
87 | repoConfigLabel.text += "
TLS CA: " + (config ? config.tlsCaPath : "not set")
88 | }
89 |
90 | function updateMetadataLabel(label, metadata, rev) {
91 | if (metadata.length === 0) {
92 | label.text = "No metadata available"
93 | return
94 | }
95 | var metadataObj = JSON.parse(metadata)
96 | label.text = ""
97 | for (var property in metadataObj)
98 | label.text += "" + property.charAt(0).toUpperCase()
99 | + property.slice(1) + ": " + metadataObj[property] + "
"
100 | label.text += "Revision: " + rev
101 | }
102 | function updateBootedMetadataLabel() {
103 | updateMetadataLabel(bootedMetadataLabel, OtaClient.bootedMetadata, OtaClient.bootedRevision)
104 | }
105 | function updateRemoteMetadataLabel() {
106 | updateMetadataLabel(remoteMetadataLabel, OtaClient.remoteMetadata, OtaClient.remoteRevision)
107 | }
108 | function updateRollbackMetadataLabel() {
109 | updateMetadataLabel(rollbackMetadataLabel, OtaClient.rollbackMetadata, OtaClient.rollbackRevision)
110 | }
111 | function updateDefaultMetadataLabel() {
112 | updateMetadataLabel(defaultMetadataLabel, OtaClient.defaultMetadata, OtaClient.defaultRevision)
113 | }
114 |
115 | Flickable {
116 | anchors.fill: parent
117 | contentHeight: topLayout.implicitHeight + topLayout.anchors.bottomMargin * 2
118 | flickableDirection: Flickable.VerticalFlick
119 | ScrollBar.vertical: ScrollBar { }
120 |
121 | ColumnLayout {
122 | id: topLayout
123 | anchors.fill: parent
124 | anchors.margins: 10
125 |
126 | Label { text: "BOOTED"; Layout.bottomMargin: 14; font.underline: true; }
127 | Label { id: bootedMetadataLabel; lineHeight : 1.3 }
128 |
129 | Label { text: "REMOTE"; Layout.bottomMargin: 14; Layout.topMargin: 14; font.underline: true }
130 | Label { id: remoteMetadataLabel; lineHeight : 1.3 }
131 |
132 | Label { text: "ROLLBACK"; Layout.bottomMargin: 14; Layout.topMargin: 14; font.underline: true }
133 | Label { id: rollbackMetadataLabel; lineHeight : 1.3 }
134 |
135 | Label { text: "DEFAULT"; Layout.bottomMargin: 14; Layout.topMargin: 14; font.underline: true }
136 | Label { id: defaultMetadataLabel; lineHeight : 1.3 }
137 |
138 | Label { text: "REPOSITORY CONFIGURATION"; Layout.bottomMargin: 14; Layout.topMargin: 14; font.underline: true }
139 | Label { id: repoConfigLabel; lineHeight : 1.3 }
140 |
141 | RowLayout {
142 | Layout.topMargin: 20
143 | Layout.bottomMargin: 10
144 |
145 | Button {
146 | text: "Use basic config"
147 | onClicked: {
148 | if (!otaEnabled())
149 | return;
150 | configureRepository(basicConfig, false)
151 | }
152 | }
153 | Button {
154 | text: "Use secure config"
155 | onClicked: {
156 | if (!otaEnabled())
157 | return;
158 | configureRepository(secureConfig, false)
159 |
160 | }
161 | }
162 | Button {
163 | text: "Fetch OTA Metadata"
164 | onClicked: {
165 | if (!otaEnabled())
166 | return;
167 | log("Fetcing OTA Metadata...")
168 | OtaClient.fetchRemoteMetadata()
169 | }
170 | }
171 | Button {
172 | text: "Update from Package"
173 | onClicked: {
174 | if (!otaEnabled())
175 | return;
176 | log("Starting update from the package ...")
177 | OtaClient.updateOffline("/var/superblock")
178 | }
179 | }
180 | Button {
181 | visible: OtaClient.rollbackAvailable
182 | text: "Rollback"
183 | onClicked: {
184 | if (!otaEnabled())
185 | return;
186 | log("Roolback...")
187 | OtaClient.rollback()
188 | }
189 | }
190 | Button {
191 | visible: OtaClient.updateAvailable
192 | text: "Update"
193 | onClicked: {
194 | if (!otaEnabled())
195 | return;
196 | log("Updating...")
197 | OtaClient.update()
198 | }
199 | }
200 | Button {
201 | visible: OtaClient.restartRequired
202 | text: "Restart"
203 | onClicked: {
204 | if (!otaEnabled())
205 | return;
206 | log("Restarting (unimplemented) ...")
207 | }
208 | }
209 | }
210 |
211 | Frame {
212 | Layout.preferredHeight: bootedMetadataLabel.font.pixelSize * 12
213 | Layout.preferredWidth: {
214 | Screen.width < 800 ? Screen.width - topLayout.anchors.leftMargin * 2
215 | : Screen.width * 0.5 > 800 ? 800 : Screen.width * 0.5
216 | }
217 | padding: 6
218 | ListView {
219 | id: logView
220 | anchors.fill: parent
221 | clip: true
222 | model: ListModel { id: logRecords }
223 | delegate: Label {
224 | text: record
225 | color: textcolor
226 | width: parent.width
227 | wrapMode: Text.WordWrap
228 | }
229 | }
230 | }
231 | }
232 | }
233 |
234 | OtaRepositoryConfig {
235 | id: basicConfig
236 | url: "http://www.b2qtupdate.com/ostree-repo"
237 | }
238 |
239 | OtaRepositoryConfig {
240 | id: secureConfig
241 | gpgVerify: true
242 | url: "https://www.b2qtupdate.com/ostree-repo"
243 | tlsClientCertPath: "/usr/share/ostree/certs/clientcert.pem"
244 | tlsClientKeyPath: "/usr/share/ostree/certs/clientkey.pem"
245 | tlsPermissive: false
246 | tlsCaPath: "/usr/share/ostree/certs/servercert.pem"
247 | }
248 |
249 | Connections {
250 | target: OtaClient
251 | onErrorChanged: logError(error)
252 | onStatusChanged: log(status)
253 | onFetchRemoteMetadataFinished: {
254 | logWithCondition("Fetching metadata from a remote server", success)
255 | if (success)
256 | log("Update available: " + OtaClient.updateAvailable)
257 | }
258 | onUpdateOfflineFinished: logWithCondition("Update from package", success)
259 | onRollbackFinished: logWithCondition("Rollback", success)
260 | onUpdateFinished: logWithCondition("Update", success)
261 | onRepositoryConfigChanged: updateConfigView(config)
262 | onRemoteMetadataChanged: updateRemoteMetadataLabel()
263 | onRollbackMetadataChanged: updateRollbackMetadataLabel()
264 | onDefaultMetadataChanged: updateDefaultMetadataLabel()
265 | }
266 |
267 | Component.onCompleted: {
268 | if (!OtaClient.otaEnabled) {
269 | log("OTA Update functionality is not enabled on this device")
270 | return;
271 | }
272 |
273 | var configuredOk = configureRepository(basicConfig, true);
274 | if (!configuredOk)
275 | // Already configured, so won't be handled by onRepositoryConfigChanged.
276 | // But config view still needs to be updated.
277 | updateConfigView(OtaClient.repositoryConfig())
278 |
279 | updateBootedMetadataLabel()
280 | updateRemoteMetadataLabel()
281 | updateRollbackMetadataLabel()
282 | updateDefaultMetadataLabel()
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/examples/qml/basic/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ota.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 | SUBDIRS = \
3 | doc \
4 | src
5 |
--------------------------------------------------------------------------------
/qt-ostree/build-ostree.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #############################################################################
3 | ##
4 | ## Copyright (C) 2016 The Qt Company Ltd.
5 | ## Contact: https://www.qt.io/licensing/
6 | ##
7 | ## This file is part of the Qt OTA Update module of the Qt Toolkit.
8 | ##
9 | ## $QT_BEGIN_LICENSE:GPL$
10 | ## Commercial License Usage
11 | ## Licensees holding valid commercial Qt licenses may use this file in
12 | ## accordance with the commercial license agreement provided with the
13 | ## Software or, alternatively, in accordance with the terms contained in
14 | ## a written agreement between you and The Qt Company. For licensing terms
15 | ## and conditions see https://www.qt.io/terms-conditions. For further
16 | ## information use the contact form at https://www.qt.io/contact-us.
17 | ##
18 | ## GNU General Public License Usage
19 | ## Alternatively, this file may be used under the terms of the GNU
20 | ## General Public License version 3 or (at your option) any later version
21 | ## approved by the KDE Free Qt Foundation. The licenses are as published by
22 | ## the Free Software Foundation and appearing in the file LICENSE.GPL3
23 | ## included in the packaging of this file. Please review the following
24 | ## information to ensure the GNU General Public License requirements will
25 | ## be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 | ##
27 | ## $QT_END_LICENSE$
28 | ##
29 | #############################################################################
30 | set -e
31 | set -x
32 |
33 | # On Ubuntu 14.04 the following build dependecies needs to be installed:
34 | # sudo apt-get install git autoconf gtk-doc-tools libattr1-dev libcap-dev libghc-gio-dev liblzma-dev \
35 | # e2fslibs-dev libgpgme11-dev libsoup2.4-dev libarchive-dev
36 |
37 | DIR=$(dirname $(readlink -f $0))
38 | OTA_OSTREE_REF="8ece4d6d51bdbe3e41ab318259276bb83e553aa0"
39 | PARALLEL=4
40 |
41 | cd "${DIR}"
42 | rm -rf ostree-git
43 |
44 | git clone https://github.com/ostreedev/ostree.git ostree-git
45 | cd ostree-git
46 | git checkout ${OTA_OSTREE_REF}
47 | git am "${DIR}"/patches/ostree-prepare-root-enabler-for-simpler-kernel-arg.patch
48 | git am "${DIR}"/patches/deploy-add-karg-none-argument.patch
49 | git am "${DIR}"/patches/Support-for-booting-without-initramfs.patch
50 | git am "${DIR}"/patches/Allow-updating-files-in-the-boot-directory.patch
51 | git am "${DIR}"/patches/u-boot-add-bootdir-to-the-generated-uEnv.txt.patch
52 | git am "${DIR}"/patches/Create-firmware-convenience-symlinks.patch
53 | git am "${DIR}"/patches/deltas-Expose-the-filename-parameter.patch
54 |
55 | ./autogen.sh \
56 | --with-soup \
57 | --enable-rofiles-fuse=no \
58 | --enable-gtk-doc-html=no \
59 | --enable-man=no \
60 | --enable-introspection=no \
61 | --disable-shared \
62 | --enable-static
63 |
64 | make V=1 -j ${PARALLEL}
65 | cd -
66 |
67 | mv ostree-git/ostree .
68 | strip ostree
69 | rm -rf ostree-git
70 |
--------------------------------------------------------------------------------
/qt-ostree/ostree-grub-generator:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #############################################################################
3 | ##
4 | ## Copyright (C) 2016 The Qt Company Ltd.
5 | ## Contact: https://www.qt.io/licensing/
6 | ##
7 | ## This file is part of the Qt OTA Update module of the Qt Toolkit.
8 | ##
9 | ## $QT_BEGIN_LICENSE:GPL$
10 | ## Commercial License Usage
11 | ## Licensees holding valid commercial Qt licenses may use this file in
12 | ## accordance with the commercial license agreement provided with the
13 | ## Software or, alternatively, in accordance with the terms contained in
14 | ## a written agreement between you and The Qt Company. For licensing terms
15 | ## and conditions see https://www.qt.io/terms-conditions. For further
16 | ## information use the contact form at https://www.qt.io/contact-us.
17 | ##
18 | ## GNU General Public License Usage
19 | ## Alternatively, this file may be used under the terms of the GNU
20 | ## General Public License version 3 or (at your option) any later version
21 | ## approved by the KDE Free Qt Foundation. The licenses are as published by
22 | ## the Free Software Foundation and appearing in the file LICENSE.GPL3
23 | ## included in the packaging of this file. Please review the following
24 | ## information to ensure the GNU General Public License requirements will
25 | ## be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 | ##
27 | ## $QT_END_LICENSE$
28 | ##
29 | #############################################################################
30 |
31 | # This script is called by ostree/src/libostree/ostree-bootloader-grub2.c whenever
32 | # boot loader configuration file needs to be updated. It can be used as a template
33 | # for a custom grub.cfg generator. When writing a custom grub.cfg generator:
34 | #
35 | # 1) cp ostree-grub-generator ostree-grub-generator-mydevice
36 | # 2) Edit ostree-grub-generator-mydevice
37 | # 3) Pass ostree-grub-generator-mydevice to qt-ostree via --grub2-cfg-generator
38 | # 4) Examine the generated grub.cfg file in the OTA sysroot (created by --create-ota-sysroot)
39 | #
40 | # WARNINGS:
41 | #
42 | # - Do not modify populate_menu() function, unless you know what you are doing. This function
43 | # converts boot loader entries (https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/)
44 | # into GRUB2 menuentry sections.
45 | #
46 | # - Be aware that this script is executed not only on a host machine, but also on a
47 | # target device, thus think about shell portability. Target device for example might
48 | # be using busybox with a limited shell.
49 |
50 | set -e
51 |
52 | script=$(basename ${0})
53 | # Atomically safe location where to generete grub.cfg when executing OTA update.
54 | new_grub2_cfg=${2}
55 | entries_path=$(dirname $new_grub2_cfg)/entries
56 |
57 | read_config()
58 | {
59 | config_file=${1}
60 | title=""
61 | initrd=""
62 | options=""
63 | linux=""
64 |
65 | while read -r line
66 | do
67 | record=$(echo ${line} | cut -f 1 -d ' ')
68 | value=$(echo ${line} | cut -s -f2- -d ' ')
69 | case "${record}" in
70 | "title")
71 | title=${value}
72 | ;;
73 | "initrd")
74 | initrd=${value}
75 | ;;
76 | "linux")
77 | linux=${value}
78 | ;;
79 | "options")
80 | options=${value}
81 | ;;
82 | esac
83 | done < ${config_file}
84 |
85 | if [ -z "${title}" ]; then
86 | title="(Untitled)"
87 | fi
88 | }
89 |
90 | populate_menu()
91 | {
92 | # We need to prepend "boot/" prefix when boot/ directory is on the same
93 | # partition as usr/ - this is the layout we have on Intel NUC boards, the
94 | # only board with GRUB boot loader that we support at the moment. When there
95 | # will be more boards, determine boot_prefix at runtime.
96 | boot_prefix="/boot"
97 |
98 | for config in $(ls $entries_path/*.conf); do
99 | read_config ${config}
100 | menu="${menu}menuentry '${title}' {\n"
101 | menu="${menu}\t linux ${boot_prefix}/${linux} ${options}\n"
102 | if [ -n "${initrd}" ] ; then
103 | menu="${menu}\t initrd ${boot_prefix}/${initrd}\n"
104 | fi
105 | menu="${menu}}\n\n"
106 | done
107 | # printf seems to be more reliable across shells for special character (\n, \t) evaluation
108 | printf "$menu" >> ${new_grub2_cfg}
109 | }
110 |
111 | populate_warning()
112 | {
113 | cat >> ${new_grub2_cfg} <> ${new_grub2_cfg} <
3 | Date: Mon, 22 Aug 2016 11:32:16 +0200
4 | Subject: [PATCH 1/3] Allow updating files in the /boot directory
5 |
6 | This patch adds support for copying (or hardlinking on
7 | single partition systems) all files from the deployment's
8 | /usr/lib/ostree-boot directory to the relevant
9 | /boot/ostree/$os-$bootcsum/ directory. This feature can
10 | be enabled by 'touch .ostree-bootcsumdir-source' in
11 | /usr/lib/ostree-boot.
12 | ---
13 | src/libostree/ostree-sysroot-deploy.c | 101 +++++++++++++++++++++++++++++++---
14 | 1 file changed, 94 insertions(+), 7 deletions(-)
15 |
16 | diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
17 | index a05ca30..f34e3f3 100644
18 | --- a/src/libostree/ostree-sysroot-deploy.c
19 | +++ b/src/libostree/ostree-sysroot-deploy.c
20 | @@ -165,12 +165,31 @@ dirfd_copy_attributes_and_xattrs (int src_parent_dfd,
21 | }
22 |
23 | static gboolean
24 | +hardlink_or_copy_dir_recurse (int src_parent_dfd,
25 | + int dest_parent_dfd,
26 | + const char *name,
27 | + gboolean hardlink,
28 | + GCancellable *cancellable,
29 | + GError **error);
30 | +
31 | +static gboolean
32 | copy_dir_recurse (int src_parent_dfd,
33 | int dest_parent_dfd,
34 | const char *name,
35 | GCancellable *cancellable,
36 | GError **error)
37 | {
38 | + return hardlink_or_copy_dir_recurse (src_parent_dfd, dest_parent_dfd, name, FALSE, cancellable, error);
39 | +}
40 | +
41 | +static gboolean
42 | +hardlink_or_copy_dir_recurse (int src_parent_dfd,
43 | + int dest_parent_dfd,
44 | + const char *name,
45 | + gboolean hardlink,
46 | + GCancellable *cancellable,
47 | + GError **error)
48 | +{
49 | g_auto(GLnxDirFdIterator) src_dfd_iter = { 0, };
50 | glnx_fd_close int dest_dfd = -1;
51 | struct dirent *dent;
52 | @@ -210,17 +229,27 @@ copy_dir_recurse (int src_parent_dfd,
53 |
54 | if (S_ISDIR (child_stbuf.st_mode))
55 | {
56 | - if (!copy_dir_recurse (src_dfd_iter.fd, dest_dfd, dent->d_name,
57 | - cancellable, error))
58 | + if (!hardlink_or_copy_dir_recurse (src_dfd_iter.fd, dest_dfd, dent->d_name,
59 | + hardlink, cancellable, error))
60 | return FALSE;
61 | }
62 | else
63 | {
64 | - if (!glnx_file_copy_at (src_dfd_iter.fd, dent->d_name, &child_stbuf,
65 | - dest_dfd, dent->d_name,
66 | - GLNX_FILE_COPY_OVERWRITE,
67 | - cancellable, error))
68 | - return FALSE;
69 | + if (hardlink)
70 | + {
71 | + if (!hardlink_or_copy_at (src_dfd_iter.fd, dent->d_name,
72 | + dest_dfd, dent->d_name,
73 | + cancellable, error))
74 | + return FALSE;
75 | + }
76 | + else
77 | + {
78 | + if (!glnx_file_copy_at (src_dfd_iter.fd, dent->d_name, &child_stbuf,
79 | + dest_dfd, dent->d_name,
80 | + GLNX_FILE_COPY_OVERWRITE,
81 | + cancellable, error))
82 | + return FALSE;
83 | + }
84 | }
85 | }
86 |
87 | @@ -1301,6 +1330,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
88 | g_autofree char *version_key = NULL;
89 | g_autofree char *ostree_kernel_arg = NULL;
90 | g_autofree char *options_key = NULL;
91 | + g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
92 | GString *title_key;
93 | __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL;
94 | const char *val;
95 | @@ -1367,6 +1397,63 @@ install_deployment_kernel (OstreeSysroot *sysroot,
96 | }
97 | }
98 |
99 | + if (fstatat (tree_boot_dfd, ".ostree-bootcsumdir-source", &stbuf, 0) == 0)
100 | + {
101 | + if (!glnx_dirfd_iterator_init_at (tree_boot_dfd, ".", FALSE, &dfd_iter, error))
102 | + goto out;
103 | +
104 | + while (TRUE)
105 | + {
106 | + struct dirent *dent;
107 | +
108 | + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
109 | + goto out;
110 | + if (dent == NULL)
111 | + break;
112 | +
113 | + /* Skip special files - vmlinuz-* and initramfs-* are handled separately */
114 | + if (g_str_has_prefix (dent->d_name, "vmlinuz-") || g_str_has_prefix (dent->d_name, "initramfs-"))
115 | + continue;
116 | +
117 | + if (fstatat (bootcsum_dfd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
118 | + {
119 | + if (errno != ENOENT)
120 | + {
121 | + glnx_set_prefix_error_from_errno (error, "fstatat %s", dent->d_name);
122 | + goto out;
123 | + }
124 | +
125 | + if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
126 | + {
127 | + glnx_set_error_from_errno (error);
128 | + goto out;
129 | + }
130 | +
131 | + if (S_ISDIR (stbuf.st_mode))
132 | + {
133 | + if (!hardlink_or_copy_dir_recurse (tree_boot_dfd, bootcsum_dfd, dent->d_name,
134 | + TRUE, cancellable, error))
135 | + goto out;
136 | + }
137 | + else
138 | + {
139 | + if (!hardlink_or_copy_at (tree_boot_dfd, dent->d_name,
140 | + bootcsum_dfd, dent->d_name,
141 | + cancellable, error))
142 | + goto out;
143 | + }
144 | + }
145 | + }
146 | + }
147 | + else
148 | + {
149 | + if (errno != ENOENT)
150 | + {
151 | + glnx_set_prefix_error_from_errno (error, "fstatat %s", ".ostree-bootcsumdir-source");
152 | + goto out;
153 | + }
154 | + }
155 | +
156 | if (fstatat (deployment_dfd, "usr/lib/os-release", &stbuf, 0) != 0)
157 | {
158 | if (errno != ENOENT)
159 | --
160 | 2.7.4
161 |
162 |
--------------------------------------------------------------------------------
/qt-ostree/patches/Create-firmware-convenience-symlinks.patch:
--------------------------------------------------------------------------------
1 | From c4df63488b9e09a9aa69e32ea5c0671c9dc50c9d Mon Sep 17 00:00:00 2001
2 | From: Gatis Paeglis
3 | Date: Wed, 24 Aug 2016 12:29:38 +0200
4 | Subject: [PATCH] Create firmware convenience symlinks.
5 |
6 | Later this could be moved into utils or a similar
7 | location, if other boot loader backends will need
8 | this functionality.
9 | ---
10 | src/libostree/ostree-bootloader-uboot.c | 93 ++++++++++++++++++++++++++++++++-
11 | 1 file changed, 92 insertions(+), 1 deletion(-)
12 |
13 | diff --git a/src/libostree/ostree-bootloader-uboot.c b/src/libostree/ostree-bootloader-uboot.c
14 | index 22251da..26a3127 100644
15 | --- a/src/libostree/ostree-bootloader-uboot.c
16 | +++ b/src/libostree/ostree-bootloader-uboot.c
17 | @@ -62,6 +62,97 @@ _ostree_bootloader_uboot_get_name (OstreeBootloader *bootloader)
18 | return "U-Boot";
19 | }
20 |
21 | +/* It is common for firmware to search / on the boot partition for additional
22 | + * files that are required for booting. It can be difficult to change this search
23 | + * logic if it is hardcoded somewhere low in the stack or is in a read-only memory.
24 | + * This issue can be solved by system builders by creating a convenience symlink:
25 | + *
26 | + * cd sysroot/boot
27 | + * ln -s loader/second-stage-bootloader second-stage-bootloader
28 | + *
29 | + * This function will make sure that loader/second-stage-bootloader points to the
30 | + * correct target file version. This function does nothing if boot/ does not contain
31 | + * symlink files pointing into the loader/ directory.
32 | + */
33 | +static gboolean
34 | +create_firmware_convenience_symlinks (OstreeBootloaderUboot *self,
35 | + char *bootcsum_dir,
36 | + int bootversion,
37 | + GCancellable *cancellable,
38 | + GError **error)
39 | +{
40 | + glnx_fd_close int loader_dfd = -1;
41 | + glnx_fd_close int boot_dfd = -1;
42 | + g_autofree char *loader_dir = NULL;
43 | + g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
44 | +
45 | + loader_dir = g_strdup_printf ("boot/loader.%d/", bootversion);
46 | + if (!glnx_opendirat (self->sysroot->sysroot_fd, loader_dir, FALSE, &loader_dfd, error))
47 | + return FALSE;
48 | + if (!glnx_opendirat (self->sysroot->sysroot_fd, "boot", FALSE, &boot_dfd, error))
49 | + return FALSE;
50 | + if (!glnx_dirfd_iterator_init_take_fd (dup (boot_dfd), &dfd_iter, error))
51 | + return FALSE;
52 | +
53 | + while (TRUE) {
54 | + struct dirent *dent;
55 | + struct stat stbuf;
56 | +
57 | + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
58 | + return FALSE;
59 | + if (dent == NULL)
60 | + break;
61 | +
62 | + if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
63 | + {
64 | + if (errno == ENOENT)
65 | + continue;
66 | + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "fstatat");
67 | + return FALSE;
68 | + }
69 | +
70 | + if (S_ISLNK(stbuf.st_mode))
71 | + {
72 | + char path_buffer[PATH_MAX];
73 | + g_autofree char *symlink_target = NULL;
74 | + symlink_target = glnx_readlinkat_malloc (boot_dfd, dent->d_name, cancellable, error);
75 | +
76 | + if (g_str_has_prefix (symlink_target, "loader/"))
77 | + {
78 | + if (g_strcmp0 (dent->d_name, "uEnv.txt") == 0)
79 | + continue;
80 | +
81 | + snprintf (path_buffer, sizeof(path_buffer), "%s/%s", bootcsum_dir, dent->d_name);
82 | + if (faccessat (boot_dfd, path_buffer + 1, F_OK, AT_SYMLINK_NOFOLLOW) == -1)
83 | + {
84 | + /* This bootcsum dir does not contain the final target, do nothing. */
85 | + if (errno == ENOENT)
86 | + continue;
87 | + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "faccessat");
88 | + return FALSE;
89 | + }
90 | +
91 | + /* Cleanup from stray symlinks. This can happend when the previous deployment was
92 | + interrupted and no cleanup routines were run before restaring the deployment. */
93 | + if (unlinkat (loader_dfd, dent->d_name, 0) == -1 && errno != ENOENT)
94 | + {
95 | + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "unlinkat");
96 | + return FALSE;
97 | + }
98 | + /* Complete the link chain to the current boot file version. */
99 | + snprintf (path_buffer, sizeof(path_buffer), "..%s/%s", bootcsum_dir, dent->d_name);
100 | + if (symlinkat (path_buffer, loader_dfd, dent->d_name) == -1)
101 | + {
102 | + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "symlinkat");
103 | + return FALSE;
104 | + }
105 | + }
106 | + }
107 | + }
108 | +
109 | + return TRUE;
110 | +}
111 | +
112 | static gboolean
113 | create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
114 | int bootversion,
115 | @@ -138,7 +229,7 @@ create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
116 | }
117 | }
118 |
119 | - return TRUE;
120 | + return create_firmware_convenience_symlinks (self, bootdir, bootversion, cancellable, error);
121 | }
122 |
123 | static gboolean
124 | --
125 | 2.7.4
126 |
127 |
--------------------------------------------------------------------------------
/qt-ostree/patches/Support-for-booting-without-initramfs.patch:
--------------------------------------------------------------------------------
1 | From a31c9083870fd934e242cc9cc56fdd39ad0a42cb Mon Sep 17 00:00:00 2001
2 | From: Gatis Paeglis
3 | Date: Wed, 24 Aug 2016 12:00:14 +0200
4 | Subject: [PATCH 3/4] Support for booting without initramfs
5 |
6 | Previously when initramfs-* was not found in a deployment's
7 | boot directory, it was assumed that rootfs is prepared for
8 | ostree booting by a kernel patch.
9 |
10 | With this patch, the behaviour changes to be - if initramfs-*
11 | is not found, assume that system is using a static
12 | ostree-prepare-root as init process. Booting without initramfs
13 | is a common use case on embedded systems. This approach is
14 | also more convenient, than having to patch the kernel.
15 | ---
16 | Makefile-switchroot.am | 3 +++
17 | configure.ac | 8 ++++++++
18 | src/boot/grub2/ostree-grub-generator | 8 +++++---
19 | src/libostree/ostree-sysroot-deploy.c | 18 +++++++++++++-----
20 | 4 files changed, 29 insertions(+), 8 deletions(-)
21 |
22 | diff --git a/Makefile-switchroot.am b/Makefile-switchroot.am
23 | index ef837ce..70a6de7 100644
24 | --- a/Makefile-switchroot.am
25 | +++ b/Makefile-switchroot.am
26 | @@ -29,6 +29,9 @@ libswitchroot_mountutil_la_SOURCES = \
27 | ostree_prepare_root_SOURCES = src/switchroot/ostree-prepare-root.c
28 | ostree_prepare_root_LDADD = libswitchroot-mountutil.la
29 | ostree_prepare_root_CFLAGS = $(AM_CFLAGS) -Isrc/switchroot
30 | +if BUILDOPT_STATIC_PREPARE_ROOT
31 | +ostree_prepare_root_LDFLAGS = --static
32 | +endif
33 |
34 | ostree_remount_SOURCES = src/switchroot/ostree-remount.c
35 | ostree_remount_LDADD = libswitchroot-mountutil.la
36 | diff --git a/configure.ac b/configure.ac
37 | index 4831bcc..97f3112 100644
38 | --- a/configure.ac
39 | +++ b/configure.ac
40 | @@ -294,6 +294,13 @@ AS_IF([test x$with_grub2_mkconfig_path = x], [
41 | ],[GRUB2_MKCONFIG=$with_grub2_mkconfig_path])
42 | AC_DEFINE_UNQUOTED([GRUB2_MKCONFIG_PATH], ["$GRUB2_MKCONFIG"], [The system grub2-mkconfig executible name])
43 |
44 | +AC_ARG_WITH(static-prepare-root,
45 | + AS_HELP_STRING([--with-static-prepare-root],
46 | + [Build static version of the 'ostree-prepare-root' binary. Useful when
47 | + using 'ostree-prepare-root' as the init (PID 1) process. (default: no)]),,
48 | + [with_static_prepare_root=no])
49 | +AM_CONDITIONAL(BUILDOPT_STATIC_PREPARE_ROOT, test x$with_static_prepare_root = xyes)
50 | +
51 | dnl for tests
52 | AS_IF([test "x$found_introspection" = xyes], [
53 | AC_PATH_PROG(GJS, [gjs])
54 | @@ -327,6 +334,7 @@ echo "
55 | libarchive (parse tar files directly): $with_libarchive
56 | static deltas: yes (always enabled now)
57 | O_TMPFILE: $enable_otmpfile
58 | + static ostree-prepare-root $with_static_prepare_root
59 | man pages (xsltproc): $enable_man
60 | api docs (gtk-doc): $enable_gtk_doc
61 | gjs-based tests: $have_gjs
62 | diff --git a/src/boot/grub2/ostree-grub-generator b/src/boot/grub2/ostree-grub-generator
63 | index 5673b26..ceca806 100644
64 | --- a/src/boot/grub2/ostree-grub-generator
65 | +++ b/src/boot/grub2/ostree-grub-generator
66 | @@ -28,7 +28,7 @@ entries_path=$(dirname $new_grub2_cfg)/entries
67 |
68 | read_config()
69 | {
70 | - config_file=${entries_path}/${1}
71 | + config_file=${1}
72 | title=""
73 | initrd=""
74 | options=""
75 | @@ -62,11 +62,13 @@ read_config()
76 | populate_menu()
77 | {
78 | boot_prefix="${OSTREE_BOOT_PARTITION}"
79 | - for config in $(ls ${entries_path}); do
80 | + for config in $(ls $entries_path/*.conf); do
81 | read_config ${config}
82 | menu="${menu}menuentry '${title}' {\n"
83 | menu="${menu}\t linux ${boot_prefix}${linux} ${options}\n"
84 | - menu="${menu}\t initrd ${boot_prefix}${initrd}\n"
85 | + if [ -n "${initrd}" ] ; then
86 | + menu="${menu}\t initrd ${boot_prefix}${initrd}\n"
87 | + fi
88 | menu="${menu}}\n\n"
89 | done
90 | # The printf command seems to be more reliable across shells for special character (\n, \t) evaluation
91 | diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
92 | index a05ca30..c0a0347 100644
93 | --- a/src/libostree/ostree-sysroot-deploy.c
94 | +++ b/src/libostree/ostree-sysroot-deploy.c
95 | @@ -1458,20 +1458,28 @@ install_deployment_kernel (OstreeSysroot *sysroot,
96 | ostree_bootconfig_parser_set (bootconfig, "linux", boot_relpath);
97 | }
98 |
99 | + val = ostree_bootconfig_parser_get (bootconfig, "options");
100 | + kargs = _ostree_kernel_args_from_string (val);
101 | +
102 | if (dest_initramfs_name)
103 | {
104 | g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", dest_initramfs_name, NULL);
105 | ostree_bootconfig_parser_set (bootconfig, "initrd", boot_relpath);
106 | }
107 | -
108 | - val = ostree_bootconfig_parser_get (bootconfig, "options");
109 | + else
110 | + {
111 | + g_autofree char *prepare_root_arg = NULL;
112 | + prepare_root_arg = g_strdup_printf ("init=/ostree/boot.%d/%s/%s/%d/usr/lib/ostree/ostree-prepare-root",
113 | + new_bootversion, osname, bootcsum,
114 | + ostree_deployment_get_bootserial (deployment));
115 | + _ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg));
116 | + }
117 |
118 | ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d",
119 | new_bootversion, osname, bootcsum,
120 | ostree_deployment_get_bootserial (deployment));
121 | - kargs = _ostree_kernel_args_from_string (val);
122 | - _ostree_kernel_args_replace_take (kargs, ostree_kernel_arg);
123 | - ostree_kernel_arg = NULL;
124 | + _ostree_kernel_args_replace_take (kargs, g_steal_pointer (&ostree_kernel_arg));
125 | +
126 | options_key = _ostree_kernel_args_to_string (kargs);
127 | ostree_bootconfig_parser_set (bootconfig, "options", options_key);
128 |
129 | --
130 | 2.7.4
131 |
132 |
--------------------------------------------------------------------------------
/qt-ostree/patches/deltas-Expose-the-filename-parameter.patch:
--------------------------------------------------------------------------------
1 | From 4537b1d2c89d3c9b24f0c08874ec6a1f720add0a Mon Sep 17 00:00:00 2001
2 | From: Gatis Paeglis
3 | Date: Thu, 24 Nov 2016 15:59:36 +0100
4 | Subject: [PATCH] deltas: Expose the filename parameter
5 |
6 | The C API (ostree_repo_static_delta_generate) knows what to do
7 | with it, but this parameter was never exposed via command line
8 | tool.
9 | ---
10 | src/ostree/ot-builtin-static-delta.c | 5 +++++
11 | 1 file changed, 5 insertions(+)
12 |
13 | diff --git a/src/ostree/ot-builtin-static-delta.c b/src/ostree/ot-builtin-static-delta.c
14 | index a1c220b..57799d4 100644
15 | --- a/src/ostree/ot-builtin-static-delta.c
16 | +++ b/src/ostree/ot-builtin-static-delta.c
17 | @@ -33,6 +33,7 @@ static char *opt_min_fallback_size;
18 | static char *opt_max_bsdiff_size;
19 | static char *opt_max_chunk_size;
20 | static char *opt_endianness;
21 | +static char *opt_filename;
22 | static gboolean opt_empty;
23 | static gboolean opt_swap_endianness;
24 | static gboolean opt_inline;
25 | @@ -71,6 +72,7 @@ static GOptionEntry generate_options[] = {
26 | { "min-fallback-size", 0, 0, G_OPTION_ARG_STRING, &opt_min_fallback_size, "Minimum uncompressed size in megabytes for individual HTTP request", NULL},
27 | { "max-bsdiff-size", 0, 0, G_OPTION_ARG_STRING, &opt_max_bsdiff_size, "Maximum size in megabytes to consider bsdiff compression for input files", NULL},
28 | { "max-chunk-size", 0, 0, G_OPTION_ARG_STRING, &opt_max_chunk_size, "Maximum size of delta chunks in megabytes", NULL},
29 | + { "filename", 0, 0, G_OPTION_ARG_STRING, &opt_filename, "Sets where to store the delta. By default gets stored in the ostree repository", NULL},
30 | { NULL }
31 | };
32 |
33 | @@ -328,6 +330,9 @@ ot_static_delta_builtin_generate (int argc, char **argv, GCancellable *cancellab
34 | if (opt_inline)
35 | g_variant_builder_add (parambuilder, "{sv}",
36 | "inline-parts", g_variant_new_boolean (TRUE));
37 | + if (opt_filename)
38 | + g_variant_builder_add (parambuilder, "{sv}",
39 | + "filename", g_variant_new_bytestring (opt_filename));
40 |
41 | g_variant_builder_add (parambuilder, "{sv}", "verbose", g_variant_new_boolean (TRUE));
42 | if (opt_endianness || opt_swap_endianness)
43 | --
44 | 2.7.4
45 |
46 |
--------------------------------------------------------------------------------
/qt-ostree/patches/deploy-add-karg-none-argument.patch:
--------------------------------------------------------------------------------
1 | From 9ca3a2cc64bc709649d0d756fa715aaef807dca8 Mon Sep 17 00:00:00 2001
2 | From: Gatis Paeglis
3 | Date: Fri, 12 Aug 2016 11:51:04 +0200
4 | Subject: [PATCH 2/4] deploy: add --karg-none argument
5 |
6 | If the current deployment has "rootwait root=/dev/sda2",
7 | but the new deployment does not need "rootwait" anymore,
8 | there is no way to clear this arg at the moment (as opposed
9 | to "karg=root=", which overrides any earlier argument with
10 | the same name). With "--karg-none" users can now clear all
11 | the previous args and set new "root=":
12 |
13 | ostree admin deploy --karg-none --karg=root=LABEL=rootfs
14 | ---
15 | src/ostree/ot-admin-builtin-deploy.c | 10 +++++++++-
16 | 1 file changed, 9 insertions(+), 1 deletion(-)
17 |
18 | diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c
19 | index c66c9b3..420efa3 100644
20 | --- a/src/ostree/ot-admin-builtin-deploy.c
21 | +++ b/src/ostree/ot-admin-builtin-deploy.c
22 | @@ -38,6 +38,7 @@ static char **opt_kernel_argv_append;
23 | static gboolean opt_kernel_proc_cmdline;
24 | static char *opt_osname;
25 | static char *opt_origin_path;
26 | +static gboolean opt_kernel_arg_none;
27 |
28 | static GOptionEntry options[] = {
29 | { "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Use a different operating system root than the current one", "OSNAME" },
30 | @@ -46,6 +47,7 @@ static GOptionEntry options[] = {
31 | { "karg-proc-cmdline", 0, 0, G_OPTION_ARG_NONE, &opt_kernel_proc_cmdline, "Import current /proc/cmdline", NULL },
32 | { "karg", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv, "Set kernel argument, like root=/dev/sda1; this overrides any earlier argument with the same name", "NAME=VALUE" },
33 | { "karg-append", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv_append, "Append kernel argument; useful with e.g. console= that can be used multiple times", "NAME=VALUE" },
34 | + { "karg-none", 0, 0, G_OPTION_ARG_NONE, &opt_kernel_arg_none, "Do not import kernel arguments", NULL },
35 | { NULL }
36 | };
37 |
38 | @@ -77,6 +79,12 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
39 | goto out;
40 | }
41 |
42 | + if (opt_kernel_proc_cmdline && opt_kernel_arg_none)
43 | + {
44 | + ot_util_usage_error (context, "Can't specify both --karg-proc-cmdline and --karg-none", error);
45 | + goto out;
46 | + }
47 | +
48 | refspec = argv[1];
49 |
50 | if (!ostree_sysroot_load (sysroot, cancellable, error))
51 | @@ -135,7 +143,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
52 | if (!_ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error))
53 | goto out;
54 | }
55 | - else if (merge_deployment)
56 | + else if (merge_deployment && !opt_kernel_arg_none)
57 | {
58 | OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment);
59 | g_auto(GStrv) previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1);
60 | --
61 | 2.7.4
62 |
63 |
--------------------------------------------------------------------------------
/qt-ostree/patches/ostree-prepare-root-enabler-for-simpler-kernel-arg.patch:
--------------------------------------------------------------------------------
1 | From d183819e6e7bdc7a9476542cbef384285f592f3f Mon Sep 17 00:00:00 2001
2 | From: Gatis Paeglis
3 | Date: Fri, 12 Aug 2016 08:50:29 +0200
4 | Subject: [PATCH 1/4] ostree-prepare-root: enabler for simpler kernel arg
5 |
6 | With the current approach, when ostree-prepare-root is used
7 | on the kernel command line as init=, it always assumes that
8 | the next value in the argument list is a path to the sysroot.
9 | The code for falling back to a default path (if none is provided),
10 | would only work if init= is the last arg in the argument list.
11 | We can not rely on that and have to explicitly provide the
12 | path to the sysroot. Which defeats the purpose of a default
13 | path selection code.
14 |
15 | To keep command line neater assume that sysroot is on / when
16 | using ostree-prepare-root as init. This probably is what most
17 | people want anyways. Also _ostree_kernel_args* API assumes
18 | that args are space separated list. Which is problematic for:
19 | "init=${ostree}/usr/lib/ostree/ostree-prepare-root /" as it
20 | gets split in two.
21 | ---
22 | src/switchroot/ostree-prepare-root.c | 15 ++++++++++++---
23 | 1 file changed, 12 insertions(+), 3 deletions(-)
24 |
25 | diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c
26 | index 895b2e5..449fc33 100644
27 | --- a/src/switchroot/ostree-prepare-root.c
28 | +++ b/src/switchroot/ostree-prepare-root.c
29 | @@ -199,10 +199,19 @@ main(int argc, char *argv[])
30 | char srcpath[PATH_MAX];
31 | struct stat stbuf;
32 |
33 | - if (argc < 2)
34 | - root_mountpoint = "/";
35 | + if (getpid() == 1)
36 | + {
37 | + root_mountpoint = "/";
38 | + }
39 | else
40 | - root_mountpoint = argv[1];
41 | + {
42 | + if (argc < 2)
43 | + {
44 | + fprintf (stderr, "usage: ostree-prepare-root SYSROOT\n");
45 | + exit (EXIT_FAILURE);
46 | + }
47 | + root_mountpoint = argv[1];
48 | + }
49 |
50 | root_mountpoint = realpath (root_mountpoint, NULL);
51 | deploy_path = resolve_deploy_path (root_mountpoint);
52 | --
53 | 2.7.4
54 |
55 |
--------------------------------------------------------------------------------
/qt-ostree/patches/u-boot-add-bootdir-to-the-generated-uEnv.txt.patch:
--------------------------------------------------------------------------------
1 | From 8323c038733522f7f31fefc8921b7c1760416638 Mon Sep 17 00:00:00 2001
2 | From: Gatis Paeglis
3 | Date: Mon, 22 Aug 2016 15:52:21 +0200
4 | Subject: [PATCH 3/3] u-boot: add 'bootdir' to the generated uEnv.txt
5 |
6 | When doing a full copy of:
7 |
8 | $deployment/usr/lib/ostree-boot -> /boot/ostree/$os-$bootcsum/
9 |
10 | U-Boot bootscript can use the 'bootdir' to find, for example,
11 | the Device Tree (dtb) file, as in:
12 |
13 | load ${dtype} ${disk}:${bootpart} ${a_fdt} ${bootdir}${dtbname}
14 |
15 | Or u-boot external bootscript:
16 |
17 | load ${dtype} ${disk}:${bootpart} ${a_scr} ${bootdir}${scriptname}
18 |
19 | It could also be possible to point 'bootdir' directly to the
20 | $deployment/usr/lib/ostree-boot, but this would add unnecessary
21 | restrictions on what file system can be used for rootfs as u-boot,
22 | for example, can not read from BTRFS. So having
23 | bootdir=/boot/ostree/$os-$bootcsum/ is a better approach here, as
24 | /boot can be on a separate partition with its own file system type.
25 | ---
26 | src/libostree/ostree-bootloader-uboot.c | 3 +++
27 | 1 file changed, 3 insertions(+)
28 |
29 | diff --git a/src/libostree/ostree-bootloader-uboot.c b/src/libostree/ostree-bootloader-uboot.c
30 | index f95ea84..0786626 100644
31 | --- a/src/libostree/ostree-bootloader-uboot.c
32 | +++ b/src/libostree/ostree-bootloader-uboot.c
33 | @@ -72,6 +72,7 @@ create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
34 | g_autoptr(GPtrArray) boot_loader_configs = NULL;
35 | OstreeBootconfigParser *config;
36 | const char *val;
37 | + g_autofree char *bootdir = NULL;
38 |
39 | if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &boot_loader_configs,
40 | cancellable, error))
41 | @@ -88,6 +89,8 @@ create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
42 | return FALSE;
43 | }
44 | g_ptr_array_add (new_lines, g_strdup_printf ("kernel_image=%s", val));
45 | + bootdir = strndup (val, strrchr(val, '/') - val);
46 | + g_ptr_array_add (new_lines, g_strdup_printf ("bootdir=%s/", bootdir));
47 |
48 | val = ostree_bootconfig_parser_get (config, "initrd");
49 | if (val)
50 | --
51 | 2.7.4
52 |
53 |
--------------------------------------------------------------------------------
/src/imports/imports.pro:
--------------------------------------------------------------------------------
1 | CXX_MODULE = qml
2 | TARGET = qtotaupdateplugin
3 | TARGETPATH = QtOtaUpdate/
4 | IMPORT_VERSION = 1.0
5 |
6 | QT += qml qtotaupdate
7 |
8 | SOURCES += pluginmain.cpp
9 |
10 | OTHER_FILES += qmldir
11 |
12 | load(qml_plugin)
13 |
--------------------------------------------------------------------------------
/src/imports/pluginmain.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #include
30 | #include
31 | #include
32 |
33 | QT_BEGIN_NAMESPACE
34 |
35 | static QObject *otaClientSingleton(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
36 | {
37 | Q_UNUSED(qmlEngine);
38 | Q_UNUSED(jsEngine);
39 | return &QOtaClient::instance();
40 | }
41 |
42 | class QOTAUpdatePlugin : public QQmlExtensionPlugin
43 | {
44 | Q_OBJECT
45 | Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
46 |
47 | public:
48 | virtual void registerTypes(const char *uri)
49 | {
50 | Q_ASSERT(QLatin1String(uri) == QLatin1String("QtOtaUpdate"));
51 |
52 | qmlRegisterSingletonType(uri, 1, 0, "OtaClient", otaClientSingleton);
53 | qmlRegisterType(uri, 1, 0, "OtaRepositoryConfig");
54 | }
55 | };
56 |
57 | QT_END_NAMESPACE
58 |
59 | #include "pluginmain.moc"
60 |
--------------------------------------------------------------------------------
/src/imports/qmldir:
--------------------------------------------------------------------------------
1 | module QtOtaUpdate
2 | plugin qtotaupdateplugin
3 |
--------------------------------------------------------------------------------
/src/lib/lib.pro:
--------------------------------------------------------------------------------
1 | TARGET = QtOtaUpdate
2 | QT = core
3 |
4 | MODULE = qtotaupdate
5 | load(qt_module)
6 |
7 | CONFIG -= create_cmake
8 |
9 | INCLUDEPATH += \
10 | $$[QT_SYSROOT]/usr/include/ostree-1/ \
11 | $$[QT_SYSROOT]/usr/include/glib-2.0/ \
12 | $$[QT_SYSROOT]/usr/lib/glib-2.0/include
13 |
14 | LIBS += -lostree-1 -lgio-2.0 -lglib-2.0 -lgobject-2.0
15 |
16 | HEADERS += \
17 | qotaclient.h \
18 | qotaclientasync_p.h \
19 | qotaclient_p.h \
20 | qotarepositoryconfig.h \
21 | qotarepositoryconfig_p.h
22 |
23 | SOURCES += \
24 | qotaclient.cpp \
25 | qotarepositoryconfig.cpp
26 |
27 | NO_PCH_SOURCES += \
28 | qotaclientasync.cpp
29 |
--------------------------------------------------------------------------------
/src/lib/qotaclient.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #include "qotaclientasync_p.h"
30 | #include "qotaclient_p.h"
31 | #include "qotarepositoryconfig_p.h"
32 | #include "qotarepositoryconfig.h"
33 | #include "qotaclient.h"
34 |
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | QT_BEGIN_NAMESPACE
41 |
42 | Q_LOGGING_CATEGORY(qota, "b2qt.ota", QtWarningMsg)
43 |
44 | const QString repoConfigPath(QStringLiteral("/etc/ostree/remotes.d/qt-os.conf"));
45 |
46 | QOtaClientPrivate::QOtaClientPrivate(QOtaClient *client) :
47 | q_ptr(client),
48 | m_updateAvailable(false),
49 | m_rollbackAvailable(false),
50 | m_restartRequired(false)
51 | {
52 | // https://github.com/ostreedev/ostree/issues/480
53 | m_otaEnabled = QFile().exists(QStringLiteral("/ostree/deploy"));
54 | if (m_otaEnabled) {
55 | m_otaAsyncThread = new QThread();
56 | m_otaAsyncThread->start();
57 | m_otaAsync.reset(new QOtaClientAsync());
58 | m_otaAsync->moveToThread(m_otaAsyncThread);
59 | }
60 | }
61 |
62 | QOtaClientPrivate::~QOtaClientPrivate()
63 | {
64 | if (m_otaEnabled) {
65 | if (m_otaAsyncThread->isRunning()) {
66 | m_otaAsyncThread->quit();
67 | if (!Q_UNLIKELY(m_otaAsyncThread->wait(4000)))
68 | qCWarning(qota) << "Timed out waiting for worker thread to exit.";
69 | }
70 | delete m_otaAsyncThread;
71 | }
72 | }
73 |
74 | void QOtaClientPrivate::handleStateChanges()
75 | {
76 | Q_Q(QOtaClient);
77 |
78 | // The default system is used as the configuration merge source
79 | // when performing a system update.
80 | bool updateAvailable = m_defaultRev != m_remoteRev;
81 | if (m_updateAvailable != updateAvailable) {
82 | m_updateAvailable = updateAvailable;
83 | emit q->updateAvailableChanged(m_updateAvailable);
84 | }
85 |
86 | bool restartRequired = m_bootedRev != m_defaultRev;
87 | if (m_restartRequired != restartRequired) {
88 | m_restartRequired = restartRequired;
89 | emit q->restartRequiredChanged(m_restartRequired);
90 | }
91 | }
92 |
93 | void QOtaClientPrivate::setBootedMetadata(const QString &bootedRev, const QString &bootedMetadata)
94 | {
95 | m_bootedRev = bootedRev;
96 | m_bootedMetadata = bootedMetadata;
97 | }
98 |
99 | void QOtaClientPrivate::statusStringChanged(const QString &status)
100 | {
101 | Q_Q(QOtaClient);
102 | m_status = status;
103 | emit q->statusStringChanged(m_status);
104 | }
105 |
106 | void QOtaClientPrivate::errorOccurred(const QString &error)
107 | {
108 | Q_Q(QOtaClient);
109 | m_error = error;
110 | emit q->errorOccurred(m_error);
111 | }
112 |
113 | bool QOtaClientPrivate::verifyPathExist(const QString &path)
114 | {
115 | if (!QDir().exists(path)) {
116 | errorOccurred(path + QLatin1String(" does not exist"));
117 | return false;
118 | }
119 | return true;
120 | }
121 |
122 | void QOtaClientPrivate::rollbackMetadataChanged(const QString &rollbackRev, const QString &rollbackMetadata, int treeCount)
123 | {
124 | Q_Q(QOtaClient);
125 | if (m_rollbackRev == rollbackRev)
126 | return;
127 |
128 | if (!m_rollbackAvailable && treeCount > 1) {
129 | m_rollbackAvailable = true;
130 | emit q->rollbackAvailableChanged();
131 | }
132 |
133 | m_rollbackRev = rollbackRev;
134 | m_rollbackMetadata = rollbackMetadata;
135 |
136 | q->rollbackMetadataChanged();
137 | }
138 |
139 | void QOtaClientPrivate::remoteMetadataChanged(const QString &remoteRev, const QString &remoteMetadata)
140 | {
141 | Q_Q(QOtaClient);
142 | if (m_remoteRev == remoteRev)
143 | return;
144 |
145 | m_remoteRev = remoteRev;
146 | m_remoteMetadata = remoteMetadata;
147 | handleStateChanges();
148 |
149 | emit q->remoteMetadataChanged();
150 | }
151 |
152 | void QOtaClientPrivate::defaultRevisionChanged(const QString &defaultRevision, const QString &defaultMetadata)
153 | {
154 | Q_Q(QOtaClient);
155 | if (m_defaultRev == defaultRevision)
156 | return;
157 |
158 | m_defaultRev = defaultRevision;
159 | m_defaultMetadata = defaultMetadata;
160 | handleStateChanges();
161 |
162 | emit q->defaultMetadataChanged();
163 | }
164 |
165 | /*!
166 | \inqmlmodule QtOtaUpdate
167 | \qmltype OtaClient
168 | \instantiates QOtaClient
169 | \brief Main interface to the OTA functionality.
170 |
171 | OtaClient
172 | \include qotaclient.cpp client-description
173 | */
174 |
175 | /*!
176 | \class QOtaClient
177 | \inmodule qtotaupdate
178 | \brief Main interface to the OTA functionality.
179 |
180 | QOtaClient
181 | //! [client-description]
182 | provides an API to administer the system's update routines. Offline operations
183 | include querying the booted, the default and the rollback system metadata,
184 | atomically updating the system from self-contained update packages and atomically
185 | performing system rollbacks. Online operations include fetching metadata
186 | for the system on a remote server and atomically performing system updates
187 | by fetching the update via http(s).
188 |
189 | Using this API is safe and won't leave the system in an inconsistent state,
190 | even if the power fails half-way through. A device needs to be configured for
191 | it to be able to locate a server that is hosting a system update, see setRepositoryConfig().
192 |
193 | When utilizing this API from several processes, precautions need to be taken
194 | to ensure that the processes' view of the system's state is up to date, see
195 | refreshMetadata(). A typical example would be a daemon that periodically
196 | checks a remote server (with fetchRemoteMetadata()) for system updates, and
197 | then uses IPC (such as a push notification) to let the system's main GUI know
198 | when a new version is available.
199 |
200 | Any IPC mechanism of choice can be used to ensure that system's state is being
201 | modified by only a single process at a time. Methods that modify the system's
202 | state are marked as such.
203 |
204 | //! [client-description]
205 | */
206 |
207 | /*!
208 | \qmlsignal OtaClient::fetchRemoteMetadataFinished(bool success)
209 |
210 | A notifier signal for fetchRemoteMetadata(). The \a success argument indicates
211 | whether the operation was successful.
212 | */
213 |
214 | /*!
215 | \fn void QOtaClient::fetchRemoteMetadataFinished(bool success)
216 |
217 | A notifier signal for fetchRemoteMetadata(). The \a success argument indicates
218 | whether the operation was successful.
219 | */
220 |
221 | /*!
222 | \qmlsignal OtaClient::updateFinished(bool success)
223 |
224 | A notifier signal for update(). The \a success argument indicates whether
225 | the operation was successful.
226 | */
227 |
228 | /*!
229 | \fn void QOtaClient::updateFinished(bool success)
230 |
231 | A notifier signal for update(). The \a success argument indicates whether
232 | the operation was successful.
233 | */
234 |
235 | /*!
236 | \qmlsignal OtaClient::rollbackFinished(bool success)
237 |
238 | A notifier signal for rollback(). The \a success argument
239 | indicates whether the operation was successful.
240 | */
241 |
242 | /*!
243 | \fn void QOtaClient::rollbackFinished(bool success)
244 |
245 | A notifier signal for rollback(). The \a success argument
246 | indicates whether the operation was successful.
247 | */
248 |
249 | /*!
250 | \qmlsignal OtaClient::updateOfflineFinished(bool success)
251 |
252 | A notifier signal for updateOffline(). The \a success argument
253 | indicates whether the operation was successful.
254 | */
255 |
256 | /*!
257 | \fn void QOtaClient::updateOfflineFinished(bool success)
258 |
259 | A notifier signal for updateOffline(). The \a success argument
260 | indicates whether the operation was successful.
261 | */
262 |
263 | /*!
264 | \qmlsignal OtaClient::updateRemoteMetadataOfflineFinished(bool success)
265 |
266 | A notifier signal for updateRemoteMetadataOffline(). The \a success argument
267 | indicates whether the operation was successful.
268 | */
269 |
270 | /*!
271 | \fn void QOtaClient::updateRemoteMetadataOfflineFinished(bool success)
272 |
273 | A notifier signal for updateRemoteMetadataOffline(). The \a success argument
274 | indicates whether the operation was successful.
275 | */
276 |
277 | /*!
278 | \qmlsignal OtaClient::remoteMetadataChanged()
279 |
280 | This signal is emitted when remoteMetadata changes.
281 | \include qotaclient.cpp remotemetadatachanged-description
282 | */
283 |
284 | /*!
285 | \fn void QOtaClient::remoteMetadataChanged()
286 | This signal is emitted when remoteMetadata() changes.
287 | //! [remotemetadatachanged-description]
288 | This may happen when calling fetchRemoteMetadata() or updateRemoteMetadataOffline().
289 | This signal may also be emitted when calling updateOffline(), if updateRemoteMetadataOffline()
290 | haven't been called for the same package as used in updateOffline().
291 | //! [remotemetadatachanged-description]
292 | */
293 |
294 | /*!
295 | \qmlsignal OtaClient::rollbackMetadataChanged()
296 |
297 | This signal is emitted when rollbackMetadata changes. This metadata changes
298 | after system updates or rollbacks.
299 | */
300 |
301 | /*!
302 | \fn void QOtaClient::rollbackMetadataChanged()
303 |
304 | This signal is emitted when rollbackMetadata() changes. This metadata changes
305 | after system updates or rollbacks.
306 | */
307 |
308 | /*!
309 | \qmlsignal OtaClient::defaultMetadataChanged()
310 |
311 | This signal is emitted when defaultMetadata changes. This metadata changes
312 | after system updates or rollbacks.
313 | */
314 |
315 | /*!
316 | \fn void QOtaClient::defaultMetadataChanged()
317 |
318 | This signal is emitted when defaultMetadata() changes. This metadata changes
319 | after system updates or rollbacks.
320 | */
321 |
322 | /*!
323 | \qmlsignal OtaClient::updateAvailableChanged(bool available)
324 |
325 | This signal is emitted when the value of updateAvailable changes. The
326 | \a available argument holds whether a system update is available for
327 | the default system.
328 | */
329 |
330 | /*!
331 | \fn void QOtaClient::updateAvailableChanged(bool available)
332 |
333 | This signal is emitted when the value of updateAvailable changes. The
334 | \a available argument holds whether a system update is available for
335 | the default system.
336 | */
337 |
338 | /*!
339 | \qmlsignal OtaClient::rollbackAvailableChanged()
340 |
341 | This signal is emitted when the value of rollbackAvailable changes. The rollback
342 | system becomes available when a device has performed at least one system update.
343 | */
344 |
345 | /*!
346 | \fn void QOtaClient::rollbackAvailableChanged()
347 |
348 | This signal is emitted when the value of rollbackAvailable changes. The rollback
349 | system becomes available when a device has performed at least one system update.
350 | */
351 |
352 | /*!
353 | \qmlsignal OtaClient::restartRequiredChanged(bool required)
354 |
355 | This signal is emitted when the value of restartRequired changes. The
356 | \a required argument holds whether a reboot is required.
357 | */
358 |
359 | /*!
360 | \fn void QOtaClient::restartRequiredChanged(bool required)
361 |
362 | This signal is emitted when the value of restartRequired changes. The
363 | \a required argument holds whether a reboot is required.
364 | */
365 |
366 | /*!
367 | \qmlsignal OtaClient::statusChanged(string status);
368 |
369 | \include qotaclient.cpp statusstringchanged-description
370 | */
371 |
372 | /*!
373 | \fn void QOtaClient::statusStringChanged(const QString &status)
374 | //! [statusstringchanged-description]
375 | This signal is emitted when new status information is available. The
376 | \a status argument holds the status message.
377 | //! [statusstringchanged-description]
378 | */
379 |
380 | /*!
381 | \qmlsignal OtaClient::errorOccurred(string error);
382 |
383 | This signal is emitted when an error occurs. The \a error argument holds
384 | the error message.
385 | */
386 |
387 | /*!
388 | \fn void QOtaClient::errorOccurred(const QString &error)
389 |
390 | This signal is emitted when an error occurs. The \a error argument holds
391 | the error message.
392 | */
393 |
394 | /*!
395 | \qmlsignal OtaClient::repositoryConfigChanged(OtaRepositoryConfig config)
396 |
397 | This signal is emitted when the configuration file was successfully changed
398 | (\a config holds the new configuration) or removed (\a config holds the \c
399 | null value).
400 | */
401 |
402 | /*!
403 | \fn void QOtaClient::repositoryConfigChanged(QOtaRepositoryConfig *config)
404 |
405 | This signal is emitted when the configuration file was successfully changed
406 | (\a config holds a pointer to the new configuration) or removed (\a config
407 | holds the \c nullptr value).
408 | */
409 |
410 | QOtaClient::QOtaClient() :
411 | d_ptr(new QOtaClientPrivate(this))
412 | {
413 | Q_D(QOtaClient);
414 | if (d->m_otaEnabled) {
415 | QOtaClientAsync *async = d->m_otaAsync.data();
416 | connect(async, &QOtaClientAsync::fetchRemoteMetadataFinished, this, &QOtaClient::fetchRemoteMetadataFinished);
417 | connect(async, &QOtaClientAsync::updateFinished, this, &QOtaClient::updateFinished);
418 | connect(async, &QOtaClientAsync::rollbackFinished, this, &QOtaClient::rollbackFinished);
419 | connect(async, &QOtaClientAsync::updateOfflineFinished, this, &QOtaClient::updateOfflineFinished);
420 | connect(async, &QOtaClientAsync::updateRemoteMetadataOfflineFinished, this, &QOtaClient::updateRemoteMetadataOfflineFinished);
421 | connect(async, &QOtaClientAsync::errorOccurred, d, &QOtaClientPrivate::errorOccurred);
422 | connect(async, &QOtaClientAsync::statusStringChanged, d, &QOtaClientPrivate::statusStringChanged);
423 | connect(async, &QOtaClientAsync::rollbackMetadataChanged, d, &QOtaClientPrivate::rollbackMetadataChanged);
424 | connect(async, &QOtaClientAsync::remoteMetadataChanged, d, &QOtaClientPrivate::remoteMetadataChanged);
425 | connect(async, &QOtaClientAsync::defaultRevisionChanged, d, &QOtaClientPrivate::defaultRevisionChanged);
426 | d->m_otaAsync->refreshMetadata(d);
427 | }
428 | }
429 |
430 | QOtaClient::~QOtaClient()
431 | {
432 | delete d_ptr;
433 | }
434 |
435 | /*!
436 | Returns a singleton instance of QOtaClient.
437 | */
438 | QOtaClient& QOtaClient::instance()
439 | {
440 | static QOtaClient otaClient;
441 | return otaClient;
442 | }
443 |
444 | /*!
445 | \qmlmethod bool OtaClient::fetchRemoteMetadata()
446 |
447 | Fetches metadata from a remote server and updates remoteMetadata.
448 |
449 | \include qotaclient.cpp is-async-and-mutating
450 | \sa remoteMetadataChanged(), fetchRemoteMetadataFinished(), updateAvailable
451 | */
452 |
453 | /*!
454 | Fetches metadata from a remote server and updates remoteMetadata().
455 |
456 | //! [is-async-and-mutating]
457 | This method is asynchronous and returns immediately. The return value
458 | holds whether the operation was started successfully.
459 |
460 | \note This method mutates system's state.
461 | //! [is-async-and-mutating]
462 |
463 | \sa remoteMetadataChanged(), fetchRemoteMetadataFinished(), updateAvailable()
464 | */
465 | bool QOtaClient::fetchRemoteMetadata()
466 | {
467 | Q_D(QOtaClient);
468 | if (!d->m_otaEnabled)
469 | return false;
470 |
471 | d->m_otaAsync->fetchRemoteMetadata();
472 | return true;
473 | }
474 |
475 | /*!
476 | \qmlmethod bool OtaClient::update()
477 | \include qotaclient.cpp update-description
478 |
479 | \sa updateFinished(), fetchRemoteMetadata(), setRepositoryConfig(), restartRequired
480 | */
481 |
482 | /*!
483 | //! [update-description]
484 | Fetches an OTA update from a remote server and performs the system update.
485 |
486 | \include qotaclient.cpp is-async-and-mutating
487 | //! [update-description]
488 |
489 | \sa updateFinished(), fetchRemoteMetadata(), setRepositoryConfig(), restartRequired()
490 | */
491 | bool QOtaClient::update()
492 | {
493 | Q_D(const QOtaClient);
494 | if (!d->m_otaEnabled || !updateAvailable())
495 | return false;
496 |
497 | d->m_otaAsync->update(d->m_remoteRev);
498 | return true;
499 | }
500 |
501 | /*!
502 | \qmlmethod bool OtaClient::rollback()
503 | \include qotaclient.cpp rollback-description
504 |
505 | \sa rollbackFinished(), restartRequired
506 | */
507 |
508 | /*!
509 | //! [rollback-description]
510 | Rollback to the previous snapshot of the system. The currently booted system
511 | becomes the new rollback system.
512 |
513 | \include qotaclient.cpp is-async-and-mutating
514 | //! [rollback-description]
515 |
516 | \sa rollbackFinished(), restartRequired()
517 | */
518 | bool QOtaClient::rollback()
519 | {
520 | Q_D(QOtaClient);
521 | if (!d->m_otaEnabled)
522 | return false;
523 |
524 | d->m_otaAsync->rollback();
525 | return true;
526 | }
527 |
528 | /*!
529 | \qmlmethod bool OtaClient::updateOffline(string packagePath)
530 | \include qotaclient.cpp update-offline
531 | */
532 |
533 | /*!
534 | //! [update-offline]
535 | Uses the provided self-contained update package to update the system.
536 | This method is an offline counterpart for update(). The \a packagePath
537 | argument holds a path to the update package.
538 |
539 | \include qotaclient.cpp is-async-and-mutating
540 |
541 | \sa updateOfflineFinished(), updateRemoteMetadataOffline()
542 | //! [update-offline]
543 | */
544 | bool QOtaClient::updateOffline(const QString &packagePath)
545 | {
546 | Q_D(QOtaClient);
547 | if (!d->m_otaEnabled)
548 | return false;
549 |
550 | QString package = QFileInfo(packagePath).absoluteFilePath();
551 | if (!d->verifyPathExist(package))
552 | return false;
553 |
554 | d->m_otaAsync->updateOffline(package);
555 | return true;
556 | }
557 |
558 | /*!
559 | \qmlmethod bool OtaClient::updateRemoteMetadataOffline(string packagePath)
560 | Uses the provided self-contained update package to update remoteMetadata.
561 | \include qotaclient.cpp update-remote-offline
562 | */
563 |
564 | /*!
565 | Uses the provided self-contained update package to update remoteMetadata().
566 | //! [update-remote-offline]
567 | This method is an offline counterpart for fetchRemoteMetadata(). The \a packagePath
568 | argument holds a path to the update package.
569 |
570 | \include qotaclient.cpp is-async-and-mutating
571 | \sa remoteMetadataChanged(), updateRemoteMetadataOfflineFinished(), updateOffline()
572 | //! [update-remote-offline]
573 | */
574 | bool QOtaClient::updateRemoteMetadataOffline(const QString &packagePath)
575 | {
576 | Q_D(QOtaClient);
577 | if (!d->m_otaEnabled)
578 | return false;
579 |
580 | QFileInfo package(packagePath);
581 | if (!package.exists()) {
582 | d->errorOccurred(QString(QStringLiteral("The package %1 does not exist"))
583 | .arg(package.absoluteFilePath()));
584 | return false;
585 | }
586 |
587 | d->m_otaAsync->updateRemoteMetadataOffline(package.absoluteFilePath());
588 | return true;
589 | }
590 |
591 | /*!
592 | \qmlmethod bool OtaClient::refreshMetadata()
593 | \include qotaclient.cpp refresh-metadata
594 | */
595 |
596 | /*!
597 | //! [refresh-metadata]
598 | In a multi-process scenario, refreshMetadata() updates the instances' view of the system.
599 | Notifier signals are emitted for properties that depend on changed metadata.
600 | Returns \c true if metadata is refreshed successfully; otherwise returns \c false.
601 |
602 | Using this method is not required when only one process is responsible for all OTA tasks.
603 | //! [refresh-metadata]
604 | */
605 | bool QOtaClient::refreshMetadata()
606 | {
607 | Q_D(QOtaClient);
608 | if (!d->m_otaEnabled)
609 | return false;
610 |
611 | return d->m_otaAsync->refreshMetadata();
612 | }
613 |
614 | /*!
615 | \qmlmethod bool OtaClient::removeRepositoryConfig()
616 | \include qotaclient.cpp remove-repository-config
617 | */
618 |
619 | /*!
620 | //! [remove-repository-config]
621 | Remove a configuration file for the repository.
622 |
623 | The repository configuration is stored on a file system in
624 | \c {/etc/ostree/remotes.d/}\unicode {0x002A}\c{.conf}
625 |
626 | If the configuration file does not exist, this function returns \c true.
627 | If the configuration file exists, this function returns \c true if the file
628 | is removed successfully; otherwise returns \c false.
629 |
630 | \sa setRepositoryConfig(), repositoryConfigChanged()
631 | //! [remove-repository-config]
632 | */
633 | bool QOtaClient::removeRepositoryConfig()
634 | {
635 | Q_D(QOtaClient);
636 | if (!otaEnabled() || !QDir().exists(repoConfigPath))
637 | return true;
638 |
639 | bool removed = QDir().remove(repoConfigPath);
640 | if (removed)
641 | emit repositoryConfigChanged(nullptr);
642 | else
643 | d->errorOccurred(QStringLiteral("Failed to remove repository configuration"));
644 |
645 | return removed;
646 | }
647 |
648 | /*!
649 | \qmlmethod bool OtaClient::isRepositoryConfigSet(OtaRepositoryConfig config)
650 | \include qotaclient.cpp is-repository-config-set
651 | */
652 |
653 | /*!
654 | //! [is-repository-config-set]
655 | Returns \c true if the configuration \a config is already set; otherwise returns \c false.
656 | Configurations are compared by comparing all properties.
657 | //! [is-repository-config-set]
658 | */
659 | bool QOtaClient::isRepositoryConfigSet(QOtaRepositoryConfig *config) const
660 | {
661 | QScopedPointer currentConfig(repositoryConfig());
662 |
663 | bool isSet = currentConfig && config && currentConfig->url() == config->url() &&
664 | currentConfig->gpgVerify() == config->gpgVerify() &&
665 | currentConfig->tlsPermissive() == config->tlsPermissive() &&
666 | currentConfig->tlsClientCertPath() == config->tlsClientCertPath() &&
667 | currentConfig->tlsClientKeyPath() == config->tlsClientKeyPath() &&
668 | currentConfig->tlsCaPath() == config->tlsCaPath();
669 |
670 | return isSet;
671 | }
672 |
673 | /*!
674 | \qmlmethod bool OtaClient::setRepositoryConfig(OtaRepositoryConfig config)
675 | \include qotaclient.cpp set-repository-config
676 |
677 | The \a config argument is documented in OtaRepositoryConfig.
678 |
679 | \sa isRepositoryConfigSet(), removeRepositoryConfig(), repositoryConfigChanged
680 | */
681 |
682 | /*!
683 | //! [set-repository-config]
684 | Set the configuration for the repository. The repository configuration
685 | is stored on a file system in \c {/etc/ostree/remotes.d/}\unicode {0x002A}\c{.conf}.
686 | If a configuration already exists, it needs to be removed before a new configuration
687 | can be set.
688 |
689 | Returns \c true if the configuration file is set successfully; otherwise returns \c false.
690 | //! [set-repository-config]
691 |
692 | The \a config argument is documented in QOtaRepositoryConfig.
693 |
694 | \sa isRepositoryConfigSet(), removeRepositoryConfig(), repositoryConfigChanged
695 | */
696 | bool QOtaClient::setRepositoryConfig(QOtaRepositoryConfig *config)
697 | {
698 | Q_D(QOtaClient);
699 | if (!d->m_otaEnabled || !config)
700 | return false;
701 |
702 | if (QDir().exists(repoConfigPath)) {
703 | d->errorOccurred(QStringLiteral("Repository configuration already exists"));
704 | return false;
705 | }
706 | // URL
707 | if (config->url().isEmpty()) {
708 | d->errorOccurred(QStringLiteral("Repository URL can not be empty"));
709 | return false;
710 | }
711 |
712 | // TLS client certs
713 | int tlsClientArgs = 0;
714 | if (!config->tlsClientCertPath().isEmpty()) {
715 | if (!d->verifyPathExist(config->tlsClientCertPath()))
716 | return false;
717 | ++tlsClientArgs;
718 | }
719 | if (!config->tlsClientKeyPath().isEmpty()) {
720 | if (!d->verifyPathExist(config->tlsClientKeyPath()))
721 | return false;
722 | ++tlsClientArgs;
723 | }
724 | if (tlsClientArgs == 1) {
725 | d->errorOccurred(QStringLiteral("Both tlsClientCertPath and tlsClientKeyPath are required"
726 | " for TLS client authentication functionality"));
727 | return false;
728 | }
729 |
730 | // FORMAT: ostree remote add [OPTION...] NAME URL [BRANCH...]
731 | QString cmd(QStringLiteral("ostree remote add"));
732 | // GPG
733 | cmd.append(QStringLiteral(" --set=gpg-verify="));
734 | config->gpgVerify() ? cmd.append(QStringLiteral("true")) : cmd.append(QStringLiteral("false"));
735 | // TLS client authentication
736 | if (!config->tlsClientCertPath().isEmpty()) {
737 | cmd.append(QStringLiteral(" --set=tls-client-cert-path="));
738 | cmd.append(config->tlsClientCertPath());
739 | cmd.append(QStringLiteral(" --set=tls-client-key-path="));
740 | cmd.append(config->tlsClientKeyPath());
741 | }
742 | // TLS server authentication
743 | cmd.append(QStringLiteral(" --set=tls-permissive="));
744 | config->tlsPermissive() ? cmd.append(QStringLiteral("true")) : cmd.append(QStringLiteral("false"));
745 | if (!config->tlsCaPath().isEmpty()) {
746 | if (!d->verifyPathExist(config->tlsCaPath()))
747 | return false;
748 | cmd.append(QStringLiteral(" --set=tls-ca-path="));
749 | cmd.append(config->tlsCaPath());
750 | }
751 | // NAME URL [BRANCH...]
752 | cmd.append(QString(QStringLiteral(" qt-os %1 linux/qt")).arg(config->url()));
753 |
754 | bool ok = true;
755 | d->m_otaAsync->ostree(cmd, &ok);
756 | if (ok)
757 | emit repositoryConfigChanged(config);
758 |
759 | return ok;
760 | }
761 |
762 | /*!
763 | \qmlmethod OtaRepositoryConfig OtaClient::repositoryConfig()
764 |
765 | Returns a configuration object for the repository or \c null if the
766 | configuration file does not exist or could not be read.
767 |
768 | \sa setRepositoryConfig(), removeRepositoryConfig()
769 | */
770 |
771 | /*!
772 | Returns a configuration object for the repository or \c nullptr if the
773 | configuration file does not exist or could not be read. The caller is
774 | responsible for deleting the returned object.
775 |
776 | \sa setRepositoryConfig(), removeRepositoryConfig()
777 | */
778 | QOtaRepositoryConfig *QOtaClient::repositoryConfig() const
779 | {
780 | if (!otaEnabled())
781 | return nullptr;
782 | return QOtaRepositoryConfig().d_func()->repositoryConfigFromFile(repoConfigPath);
783 | }
784 |
785 | /*!
786 | \qmlproperty bool OtaClient::otaEnabled
787 | \readonly
788 |
789 | Holds whether a device supports OTA updates.
790 | */
791 |
792 | /*!
793 | \property QOtaClient::otaEnabled
794 | Holds whether a device supports OTA updates.
795 | */
796 | bool QOtaClient::otaEnabled() const
797 | {
798 | Q_D(const QOtaClient);
799 | return d->m_otaEnabled;
800 | }
801 |
802 | /*!
803 | \qmlproperty string OtaClient::error
804 | \readonly
805 |
806 | Holds the last error occurred.
807 | \sa errorOccurred()
808 | */
809 |
810 | /*!
811 | \property QOtaClient::error
812 |
813 | Holds the last error occurred.
814 | \sa errorOccurred()
815 | */
816 | QString QOtaClient::errorString() const
817 | {
818 | Q_D(const QOtaClient);
819 | return d->m_error;
820 | }
821 |
822 | /*!
823 | \qmlproperty string OtaClient::status
824 | \readonly
825 |
826 | Holds the last status message. Lengthy asynchronous operations use this property
827 | to notify of status changes.
828 |
829 | \sa statusChanged()
830 | */
831 |
832 | /*!
833 | \property QOtaClient::status
834 |
835 | Holds the last status message. Lengthy asynchronous operations use this property
836 | to notify of status changes.
837 |
838 | \sa statusStringChanged()
839 | */
840 | QString QOtaClient::statusString() const
841 | {
842 | Q_D(const QOtaClient);
843 | return d->m_status;
844 | }
845 |
846 | /*!
847 | \qmlproperty bool OtaClient::updateAvailable
848 | \readonly
849 |
850 | \include qotaclient.cpp update-available-description
851 | */
852 |
853 | /*!
854 | \property QOtaClient::updateAvailable
855 |
856 | //! [update-available-description]
857 | Holds whether a system update is available for the default system. An update is
858 | available when the default system differ from the remote system.
859 |
860 | \sa fetchRemoteMetadata(), updateAvailableChanged(), update()
861 | //! [update-available-description]
862 | */
863 | bool QOtaClient::updateAvailable() const
864 | {
865 | Q_D(const QOtaClient);
866 | if (d->m_updateAvailable)
867 | Q_ASSERT(!d->m_remoteRev.isEmpty());
868 | return d->m_updateAvailable;
869 | }
870 |
871 | /*!
872 | \qmlproperty bool OtaClient::rollbackAvailable
873 | \readonly
874 |
875 | \include qotaclient.cpp rollback-available-description
876 | */
877 |
878 | /*!
879 | \property QOtaClient::rollbackAvailable
880 |
881 | //! [rollback-available-description]
882 | Holds whether the rollback system is available.
883 |
884 | \sa rollbackAvailableChanged()
885 | //! [rollback-available-description]
886 | */
887 | bool QOtaClient::rollbackAvailable() const
888 | {
889 | Q_D(const QOtaClient);
890 | return d->m_rollbackAvailable;
891 | }
892 |
893 | /*!
894 | \qmlproperty bool OtaClient::restartRequired
895 | \readonly
896 |
897 | \include qotaclient.cpp restart-required-description
898 | */
899 |
900 | /*!
901 | \property QOtaClient::restartRequired
902 |
903 | //! [restart-required-description]
904 | Holds whether a reboot is required. Reboot is required when the default system
905 | differ from the booted system.
906 |
907 | \sa restartRequiredChanged()
908 | //! [restart-required-description]
909 | */
910 | bool QOtaClient::restartRequired() const
911 | {
912 | Q_D(const QOtaClient);
913 | return d->m_restartRequired;
914 | }
915 |
916 | /*!
917 | \qmlproperty string OtaClient::bootedRevision
918 | \readonly
919 |
920 | Holds the booted system's revision
921 | //! [what-is-revision]
922 | (a checksum in the OSTree repository representing a snapshot of the system).
923 | //! [what-is-revision]
924 |
925 | \sa bootedMetadata
926 | */
927 |
928 | /*!
929 | \property QOtaClient::bootedRevision
930 |
931 | Holds the booted system's revision
932 | \include qotaclient.cpp what-is-revision
933 |
934 | \sa bootedMetadata()
935 | */
936 |
937 | QString QOtaClient::bootedRevision() const
938 | {
939 | return d_func()->m_bootedRev;
940 | }
941 |
942 | /*!
943 | \qmlproperty string OtaClient::bootedMetadata
944 | \readonly
945 |
946 | Holds JSON-formatted metadata for the booted system.
947 | */
948 |
949 | /*!
950 | \property QOtaClient::bootedMetadata
951 |
952 | Holds JSON-formatted metadata for the snapshot of the booted system.
953 | */
954 | QString QOtaClient::bootedMetadata() const
955 | {
956 | return d_func()->m_bootedMetadata;
957 | }
958 |
959 | /*!
960 | \qmlproperty string OtaClient::remoteRevision
961 | \readonly
962 |
963 | Holds the system's revision on a server
964 | \include qotaclient.cpp what-is-revision
965 |
966 | \sa remoteMetadata
967 | */
968 |
969 | /*!
970 | \property QOtaClient::remoteRevision
971 |
972 | Holds the system's revision on a server
973 | \include qotaclient.cpp what-is-revision
974 |
975 | \sa remoteMetadata()
976 | */
977 | QString QOtaClient::remoteRevision() const
978 | {
979 | return d_func()->m_remoteRev;
980 | }
981 |
982 | /*!
983 | \qmlproperty string OtaClient::remoteMetadata
984 | \readonly
985 | \include qotaclient.cpp remote-metadata
986 | */
987 |
988 | /*!
989 | \property QOtaClient::remoteMetadata
990 | //! [remote-metadata]
991 | Holds JSON-formatted metadata for the latest snapshot of the system on a server.
992 |
993 | \sa fetchRemoteMetadata(), remoteMetadataChanged()
994 | //! [remote-metadata]
995 | */
996 | QString QOtaClient::remoteMetadata() const
997 | {
998 | return d_func()->m_remoteMetadata;
999 | }
1000 |
1001 | /*!
1002 | \qmlproperty string OtaClient::rollbackRevision
1003 | \readonly
1004 |
1005 | Holds the rollback system's revision
1006 | \include qotaclient.cpp what-is-revision
1007 |
1008 | \sa rollbackMetadata
1009 | */
1010 |
1011 | /*!
1012 | \property QOtaClient::rollbackRevision
1013 |
1014 | Holds the rollback system's revision
1015 | \include qotaclient.cpp what-is-revision
1016 |
1017 | \sa rollbackMetadata()
1018 | */
1019 | QString QOtaClient::rollbackRevision() const
1020 | {
1021 | return d_func()->m_rollbackRev;
1022 | }
1023 |
1024 | /*!
1025 | \qmlproperty string OtaClient::rollbackMetadata
1026 | \readonly
1027 | \include qotaclient.cpp rollback-metadata
1028 | */
1029 |
1030 | /*!
1031 | \property QOtaClient::rollbackMetadata
1032 | //! [rollback-metadata]
1033 | Holds JSON-formatted metadata for the snapshot of the rollback system.
1034 |
1035 | \sa rollback(), rollbackMetadataChanged()
1036 | //! [rollback-metadata]
1037 | */
1038 | QString QOtaClient::rollbackMetadata() const
1039 | {
1040 | return d_func()->m_rollbackMetadata;
1041 | }
1042 |
1043 | /*!
1044 | \qmlproperty string OtaClient::defaultRevision
1045 | \readonly
1046 |
1047 | Holds the default system's revision
1048 | \include qotaclient.cpp what-is-revision
1049 |
1050 | \sa defaultMetadata
1051 | */
1052 |
1053 | /*!
1054 | \property QOtaClient::defaultRevision
1055 |
1056 | Holds the default system's revision
1057 | \include qotaclient.cpp what-is-revision
1058 |
1059 | \sa defaultMetadata()
1060 | */
1061 | QString QOtaClient::defaultRevision() const
1062 | {
1063 | return d_func()->m_defaultRev;
1064 | }
1065 |
1066 | /*!
1067 | \qmlproperty string OtaClient::defaultMetadata
1068 | \readonly
1069 |
1070 | \include qotaclient.cpp default-metadata
1071 | */
1072 |
1073 | /*!
1074 | \property QOtaClient::defaultMetadata
1075 | //! [default-metadata]
1076 | Holds JSON-formatted metadata for the snapshot of the default system.
1077 | A default system represents what the host will boot into the next time system is rebooted.
1078 |
1079 | \sa defaultMetadataChanged()
1080 | //! [default-metadata]
1081 | */
1082 | QString QOtaClient::defaultMetadata() const
1083 | {
1084 | return d_func()->m_defaultMetadata;
1085 | }
1086 |
1087 | QT_END_NAMESPACE
1088 |
--------------------------------------------------------------------------------
/src/lib/qotaclient.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #ifndef QOTACLIENT_H
30 | #define QOTACLIENT_H
31 |
32 | #include
33 | #include
34 |
35 | QT_BEGIN_NAMESPACE
36 |
37 | class QOtaClientPrivate;
38 | class QOtaRepositoryConfig;
39 |
40 | class Q_DECL_EXPORT QOtaClient : public QObject
41 | {
42 | Q_OBJECT
43 | Q_PROPERTY(bool otaEnabled READ otaEnabled CONSTANT)
44 | Q_PROPERTY(bool updateAvailable READ updateAvailable NOTIFY updateAvailableChanged)
45 | Q_PROPERTY(bool rollbackAvailable READ rollbackAvailable NOTIFY rollbackAvailableChanged)
46 | Q_PROPERTY(bool restartRequired READ restartRequired NOTIFY restartRequiredChanged)
47 | Q_PROPERTY(QString error READ errorString NOTIFY errorOccurred)
48 | Q_PROPERTY(QString status READ statusString NOTIFY statusStringChanged)
49 | Q_PROPERTY(QString bootedRevision READ bootedRevision CONSTANT)
50 | Q_PROPERTY(QString bootedMetadata READ bootedMetadata CONSTANT)
51 | Q_PROPERTY(QString remoteRevision READ remoteRevision NOTIFY remoteMetadataChanged)
52 | Q_PROPERTY(QString remoteMetadata READ remoteMetadata NOTIFY remoteMetadataChanged)
53 | Q_PROPERTY(QString rollbackRevision READ rollbackRevision NOTIFY rollbackMetadataChanged)
54 | Q_PROPERTY(QString rollbackMetadata READ rollbackMetadata NOTIFY rollbackMetadataChanged)
55 | Q_PROPERTY(QString defaultRevision READ defaultRevision NOTIFY defaultMetadataChanged)
56 | Q_PROPERTY(QString defaultMetadata READ defaultMetadata NOTIFY defaultMetadataChanged)
57 | public:
58 | static QOtaClient& instance();
59 | virtual ~QOtaClient();
60 |
61 | bool updateAvailable() const;
62 | bool rollbackAvailable() const;
63 | bool restartRequired() const;
64 | bool otaEnabled() const;
65 | QString errorString() const;
66 | QString statusString() const;
67 |
68 | Q_INVOKABLE bool fetchRemoteMetadata();
69 | Q_INVOKABLE bool update();
70 | Q_INVOKABLE bool rollback();
71 | Q_INVOKABLE bool updateOffline(const QString &packagePath);
72 | Q_INVOKABLE bool updateRemoteMetadataOffline(const QString &packagePath);
73 | Q_INVOKABLE bool refreshMetadata();
74 | Q_INVOKABLE bool setRepositoryConfig(QOtaRepositoryConfig *config);
75 | Q_INVOKABLE bool removeRepositoryConfig();
76 | Q_INVOKABLE bool isRepositoryConfigSet(QOtaRepositoryConfig *config) const;
77 | Q_INVOKABLE QOtaRepositoryConfig *repositoryConfig() const;
78 |
79 | QString bootedRevision() const;
80 | QString bootedMetadata() const;
81 | QString remoteRevision() const;
82 | QString remoteMetadata() const;
83 | QString rollbackRevision() const;
84 | QString rollbackMetadata() const;
85 | QString defaultRevision() const;
86 | QString defaultMetadata() const;
87 |
88 | Q_SIGNALS:
89 | void remoteMetadataChanged();
90 | void rollbackMetadataChanged();
91 | void defaultMetadataChanged();
92 | void updateAvailableChanged(bool available);
93 | void rollbackAvailableChanged();
94 | void restartRequiredChanged(bool required);
95 | void statusStringChanged(const QString &status);
96 | void errorOccurred(const QString &error);
97 | void repositoryConfigChanged(QOtaRepositoryConfig *config);
98 |
99 | void fetchRemoteMetadataFinished(bool success);
100 | void updateFinished(bool success);
101 | void rollbackFinished(bool success);
102 | void updateOfflineFinished(bool success);
103 | void updateRemoteMetadataOfflineFinished(bool success);
104 |
105 | private:
106 | QOtaClient();
107 | Q_DISABLE_COPY(QOtaClient)
108 | Q_DECLARE_PRIVATE(QOtaClient)
109 | QOtaClientPrivate *const d_ptr;
110 | };
111 |
112 | QT_END_NAMESPACE
113 |
114 | #endif // QOTACLIENT_H
115 |
--------------------------------------------------------------------------------
/src/lib/qotaclient_p.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #ifndef QOTACLIENT_P_H
30 | #define QOTACLIENT_P_H
31 |
32 | #include
33 | #include
34 | #include
35 |
36 | QT_BEGIN_NAMESPACE
37 |
38 | Q_DECLARE_LOGGING_CATEGORY(qota)
39 |
40 | class QThread;
41 | class QOtaClientAsync;
42 | class QOtaRepositoryConfig;
43 | class QOtaClient;
44 |
45 | class QOtaClientPrivate : public QObject
46 | {
47 | Q_OBJECT
48 | Q_DECLARE_PUBLIC(QOtaClient)
49 | public:
50 | QOtaClientPrivate(QOtaClient *client);
51 | virtual ~QOtaClientPrivate();
52 |
53 | void handleStateChanges();
54 | void statusStringChanged(const QString &status);
55 | void errorOccurred(const QString &error);
56 | bool verifyPathExist(const QString &path);
57 | void setBootedMetadata(const QString &bootedRev, const QString &bootedMetadata);
58 | void rollbackMetadataChanged(const QString &rollbackRev, const QString &rollbackMetadata, int treeCount);
59 | void remoteMetadataChanged(const QString &remoteRev, const QString &remoteMetadata);
60 | void defaultRevisionChanged(const QString &defaultRevision, const QString &defaultMetadata);
61 |
62 | // members
63 | QOtaClient *const q_ptr;
64 | bool m_updateAvailable;
65 | bool m_rollbackAvailable;
66 | bool m_restartRequired;
67 | bool m_otaEnabled;
68 | QString m_status;
69 | QString m_error;
70 | QThread *m_otaAsyncThread;
71 | QScopedPointer m_otaAsync;
72 |
73 | QString m_bootedRev;
74 | QString m_bootedMetadata;
75 | QString m_remoteRev;
76 | QString m_remoteMetadata;
77 | QString m_rollbackRev;
78 | QString m_rollbackMetadata;
79 | QString m_defaultRev;
80 | QString m_defaultMetadata;
81 | };
82 |
83 | QT_END_NAMESPACE
84 |
85 | #endif // QOTACLIENT_P_H
86 |
--------------------------------------------------------------------------------
/src/lib/qotaclientasync.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #include "ostree-1/ostree.h"
30 | #include "glib-2.0/glib.h"
31 |
32 | #include "qotaclientasync_p.h"
33 | #include "qotaclient_p.h"
34 |
35 | #include
36 |
37 | QT_BEGIN_NAMESPACE
38 |
39 | #define OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "(uayttay)"
40 | #define OSTREE_STATIC_DELTA_FALLBACK_FORMAT "(yaytt)"
41 | #define OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT "(a{sv}tayay" OSTREE_COMMIT_GVARIANT_STRING "aya" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT ")"
42 |
43 | // from libglnx
44 | #define GLNX_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \
45 | static inline void name (void *v) \
46 | { \
47 | if (*(Type*)v) \
48 | func (*(Type*)v); \
49 | }
50 | #define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref)))
51 | GLNX_DEFINE_CLEANUP_FUNCTION0(GObject*, glnx_local_obj_unref, g_object_unref)
52 |
53 | QOtaClientAsync::QOtaClientAsync()
54 | {
55 | // async mapper
56 | connect(this, &QOtaClientAsync::fetchRemoteMetadata, this, &QOtaClientAsync::_fetchRemoteMetadata);
57 | connect(this, &QOtaClientAsync::update, this, &QOtaClientAsync::_update);
58 | connect(this, &QOtaClientAsync::rollback, this, &QOtaClientAsync::_rollback);
59 | connect(this, &QOtaClientAsync::updateOffline, this, &QOtaClientAsync::_updateOffline);
60 | connect(this, &QOtaClientAsync::updateRemoteMetadataOffline, this, &QOtaClientAsync::_updateRemoteMetadataOffline);
61 | }
62 |
63 | QOtaClientAsync::~QOtaClientAsync()
64 | {
65 | }
66 |
67 | static void parseErrorString(QString *error)
68 | {
69 | error->remove(0, qstrlen("error: "));
70 | if (error->startsWith(QLatin1String("Remote")) && error->endsWith(QLatin1String("not found")))
71 | *error = QLatin1String("Repository configuration not found");
72 | }
73 |
74 | QString QOtaClientAsync::ostree(const QString &command, bool *ok, bool updateStatus)
75 | {
76 | qCDebug(qota) << command;
77 | QProcess ostree;
78 | ostree.setProcessChannelMode(QProcess::MergedChannels);
79 | ostree.start(command);
80 | if (!ostree.waitForStarted()) {
81 | *ok = false;
82 | emit errorOccurred(QLatin1String("Failed to start: ") + command
83 | + QLatin1String(" : ") + ostree.errorString());
84 | return QString();
85 | }
86 |
87 | QString out;
88 | bool finished = false;
89 | do {
90 | finished = ostree.waitForFinished(200);
91 | if (!finished && ostree.error() != QProcess::Timedout) {
92 | *ok = false;
93 | emit errorOccurred(QLatin1String("Process failed: ") + command +
94 | QLatin1String(" : ") + ostree.errorString());
95 | return QString();
96 | }
97 | while (ostree.canReadLine()) {
98 | QByteArray bytesRead = ostree.readLine().trimmed();
99 | if (bytesRead.isEmpty())
100 | continue;
101 |
102 | QString line = QString::fromUtf8(bytesRead);
103 | qCDebug(qota) << line;
104 | if (line.startsWith(QStringLiteral("error:"))) {
105 | *ok = false;
106 | parseErrorString(&line);
107 | emit errorOccurred(line);
108 | } else {
109 | if (updateStatus)
110 | emit statusStringChanged(line);
111 | }
112 | out.append(line);
113 | }
114 | } while (!finished);
115 |
116 | return out;
117 | }
118 |
119 | OstreeSysroot* QOtaClientAsync::defaultSysroot()
120 | {
121 | GError *error = nullptr;
122 | OstreeSysroot *sysroot = ostree_sysroot_new_default();
123 | if (!ostree_sysroot_load (sysroot, 0, &error))
124 | emitGError(error);
125 | return sysroot;
126 | }
127 |
128 | QString QOtaClientAsync::metadataFromRev(const QString &rev, bool *ok)
129 | {
130 | QString jsonData;
131 | jsonData = ostree(QString(QStringLiteral("ostree cat %1 /usr/etc/qt-ota.json")).arg(rev), ok);
132 | if (jsonData.isEmpty())
133 | return jsonData;
134 |
135 | QJsonParseError parseError;
136 | QJsonDocument jsonMetadata = QJsonDocument::fromJson(jsonData.toUtf8(), &parseError);
137 | if (jsonMetadata.isNull()) {
138 | *ok = false;
139 | QString error = QString(QStringLiteral("failed to parse JSON file, error: %1, data: %2"))
140 | .arg(parseError.errorString()).arg(jsonData);
141 | emit errorOccurred(error);
142 | }
143 |
144 | QByteArray ba = jsonMetadata.toJson();
145 | return QString::fromUtf8(ba);
146 | }
147 |
148 | bool QOtaClientAsync::refreshMetadata(QOtaClientPrivate *d)
149 | {
150 | glnx_unref_object OstreeSysroot *sysroot = defaultSysroot();
151 | if (!sysroot)
152 | return false;
153 |
154 | bool ok = true;
155 | if (d) {
156 | // This is non-nullptr only when called from QOtaClient's constructor.
157 | // Booted revision can change only when a device is rebooted.
158 | OstreeDeployment *bootedDeployment = (OstreeDeployment*)ostree_sysroot_get_booted_deployment (sysroot);
159 | QString bootedRev = QLatin1String(ostree_deployment_get_csum (bootedDeployment));
160 | QString bootedMetadata = metadataFromRev(bootedRev, &ok);
161 | if (!ok)
162 | return false;
163 | d->setBootedMetadata(bootedRev, bootedMetadata);
164 | }
165 |
166 | // prepopulate with what we think is on the remote server (head of the local repo)
167 | QString remoteRev = ostree(QStringLiteral("ostree rev-parse linux/qt"), &ok);
168 | QString remoteMetadata;
169 | if (ok) remoteMetadata = metadataFromRev(remoteRev, &ok);
170 | if (!ok)
171 | return false;
172 | emit remoteMetadataChanged(remoteRev, remoteMetadata);
173 |
174 | ok = handleRevisionChanges(sysroot);
175 | return ok;
176 | }
177 |
178 | void QOtaClientAsync::_fetchRemoteMetadata()
179 | {
180 | QString remoteRev;
181 | QString remoteMetadata;
182 | bool ok = true;
183 | ostree(QStringLiteral("ostree pull --commit-metadata-only --disable-static-deltas qt-os linux/qt"), &ok);
184 | if (ok) remoteRev = ostree(QStringLiteral("ostree rev-parse linux/qt"), &ok);
185 | if (ok) ostree(QString(QStringLiteral("ostree pull --subpath=/usr/etc/qt-ota.json qt-os %1")).arg(remoteRev), &ok);
186 | if (ok) remoteMetadata = metadataFromRev(remoteRev, &ok);
187 | if (ok) emit remoteMetadataChanged(remoteRev, remoteMetadata);
188 | emit fetchRemoteMetadataFinished(ok);
189 | }
190 |
191 | bool QOtaClientAsync::deployCommit(const QString &commit, OstreeSysroot *sysroot)
192 | {
193 | bool ok = true;
194 | QString kernelArgs;
195 | GError *error = nullptr;
196 | g_autoptr(GFile) root = nullptr;
197 | OstreeRepo *repo = nullptr;
198 |
199 | // read kernel args for rev
200 | if (!ostree_sysroot_get_repo (sysroot, &repo, 0, &error) ||
201 | !ostree_repo_read_commit (repo, commit.toLatin1().constData(), &root, nullptr, nullptr, &error)) {
202 | emitGError(error);
203 | return false;
204 | }
205 | g_autoptr(GFile) kargsInRev = g_file_resolve_relative_path (root, "/usr/lib/ostree-boot/kargs");
206 | g_autoptr(GInputStream) in = (GInputStream*)g_file_read (kargsInRev, 0, 0);
207 | if (in)
208 | kernelArgs = ostree(QString(QStringLiteral("ostree cat %1 /usr/lib/ostree-boot/kargs")).arg(commit), &ok);
209 |
210 | emit statusStringChanged(QStringLiteral("Deploying..."));
211 | if (ok) ostree(QString(QStringLiteral("ostree admin deploy --karg-none %1 %2"))
212 | .arg(kernelArgs).arg(commit), &ok, true);
213 | return ok;
214 | }
215 |
216 | void QOtaClientAsync::_update(const QString &updateToRev)
217 | {
218 | glnx_unref_object OstreeSysroot *sysroot = defaultSysroot();
219 | if (!sysroot) {
220 | emit updateFinished(false);
221 | return;
222 | }
223 |
224 | bool ok = true;
225 | emit statusStringChanged(QStringLiteral("Checking for missing objects..."));
226 | ostree(QString(QStringLiteral("ostree pull qt-os:%1")).arg(updateToRev), &ok, true);
227 | if (!ok || !deployCommit(updateToRev, sysroot)) {
228 | emit updateFinished(false);
229 | return;
230 | }
231 |
232 | ok = handleRevisionChanges(sysroot, true);
233 | emit updateFinished(ok);
234 | }
235 |
236 | int QOtaClientAsync::rollbackIndex(OstreeSysroot *sysroot)
237 | {
238 | g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
239 | if (deployments->len < 2)
240 | return -1;
241 |
242 | // 1) if we're not in the default boot index (0), it plans to prepend the
243 | // booted index (1, since we can't have more than two trees) so that it
244 | // becomes index 0 (default) and the current default becomes index 1.
245 | // 2) if we're booted into the default boot index (0), let's roll back to the previous (1)
246 | return 1;
247 | }
248 |
249 | bool QOtaClientAsync::handleRevisionChanges(OstreeSysroot *sysroot, bool reloadSysroot)
250 | {
251 | if (reloadSysroot) {
252 | GError *error = nullptr;
253 | if (!ostree_sysroot_load (sysroot, 0, &error)) {
254 | emitGError(error);
255 | return false;
256 | }
257 | }
258 |
259 | g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
260 | OstreeDeployment *firstDeployment = (OstreeDeployment*)deployments->pdata[0];
261 | bool ok = true;
262 | QString defaultRev(QLatin1String(ostree_deployment_get_csum (firstDeployment)));
263 | QString defaultMetadata = metadataFromRev(defaultRev, &ok);
264 | if (!ok)
265 | return false;
266 | emit defaultRevisionChanged(defaultRev, defaultMetadata);
267 |
268 | int index = rollbackIndex(sysroot);
269 | if (index != -1) {
270 | OstreeDeployment *rollbackDeployment = (OstreeDeployment*)deployments->pdata[index];
271 | QString rollbackRev(QLatin1String(ostree_deployment_get_csum (rollbackDeployment)));
272 | QString rollbackMetadata = metadataFromRev(rollbackRev, &ok);
273 | if (!ok)
274 | return false;
275 | emit rollbackMetadataChanged(rollbackRev, rollbackMetadata, deployments->len);
276 | }
277 |
278 | return true;
279 | }
280 |
281 | void QOtaClientAsync::emitGError(GError *error)
282 | {
283 | if (!error)
284 | return;
285 |
286 | emit errorOccurred(QString::fromLatin1((error->message)));
287 | g_error_free (error);
288 | }
289 |
290 | void QOtaClientAsync::_rollback()
291 | {
292 | glnx_unref_object OstreeSysroot *sysroot = defaultSysroot();
293 | if (!sysroot) {
294 | emit rollbackFinished(false);
295 | return;
296 | }
297 |
298 | int index = rollbackIndex(sysroot);
299 | if (index == -1) {
300 | emit errorOccurred(QStringLiteral("At least 2 system versions required for rollback"));
301 | emit rollbackFinished(false);
302 | return;
303 | }
304 |
305 | g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
306 | g_autoptr(GPtrArray) newDeployments = g_ptr_array_new_with_free_func (g_object_unref);
307 | g_ptr_array_add (newDeployments, g_object_ref (deployments->pdata[index]));
308 | for (uint i = 0; i < deployments->len; i++) {
309 | if (i == (uint)index)
310 | continue;
311 | g_ptr_array_add (newDeployments, g_object_ref (deployments->pdata[i]));
312 | }
313 |
314 | // atomically update bootloader configuration
315 | GError *error = nullptr;
316 | if (!ostree_sysroot_write_deployments (sysroot, newDeployments, 0, &error)) {
317 | emitGError(error);
318 | emit rollbackFinished(false);
319 | return;
320 | }
321 |
322 | bool ok = handleRevisionChanges(sysroot, true);
323 | emit rollbackFinished(ok);
324 | }
325 |
326 | bool QOtaClientAsync::extractPackage(const QString &packagePath, OstreeSysroot *sysroot, QString *updateToRev)
327 | {
328 | GError *error = nullptr;
329 | // load delta superblock
330 | GMappedFile *mfile = g_mapped_file_new (packagePath.toLatin1().data(), FALSE, &error);
331 | if (!mfile) {
332 | emitGError(error);
333 | return false;
334 | }
335 | g_autoptr(GBytes) bytes = g_mapped_file_get_bytes (mfile);
336 | g_mapped_file_unref (mfile);
337 | g_autoptr(GVariant) deltaSuperblock = nullptr;
338 | deltaSuperblock = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
339 | bytes, FALSE);
340 | g_variant_ref_sink (deltaSuperblock);
341 |
342 | // get a timestamp of the commit object from the superblock
343 | g_autoptr(GVariant) packageCommitV = g_variant_get_child_value (deltaSuperblock, 4);
344 | if (!ostree_validate_structureof_commit (packageCommitV, &error)) {
345 | emitGError(error);
346 | return false;
347 | }
348 | guint64 packageTimestamp = ostree_commit_get_timestamp (packageCommitV);
349 | // get timestamp of the head commit from the repository
350 | bool ok = true;
351 | g_autoptr(GVariant) currentCommitV = nullptr;
352 | OstreeRepo *repo = nullptr;
353 | if (!ostree_sysroot_get_repo (sysroot, &repo, 0, &error)) {
354 | emitGError(error);
355 | return false;
356 | }
357 | QString currentCommit = ostree(QStringLiteral("ostree rev-parse linux/qt"), &ok);
358 | if (!ok || !ostree_repo_load_commit (repo, currentCommit.toLatin1().constData(),
359 | ¤tCommitV, nullptr, &error)) {
360 | emitGError(error);
361 | return false;
362 | }
363 | guint64 currentTimestamp = ostree_commit_get_timestamp (currentCommitV);
364 | qCDebug(qota) << "current timestamp:" << currentTimestamp;
365 | qCDebug(qota) << "package timestamp:" << packageTimestamp;
366 | if (packageTimestamp < currentTimestamp) {
367 | emit errorOccurred(QString(QStringLiteral("Not allowed to downgrade - current timestamp: %1,"
368 | " package timestamp: %2")).arg(currentTimestamp).arg(packageTimestamp));
369 | return false;
370 | }
371 |
372 | emit statusStringChanged(QStringLiteral("Extracting the update package..."));
373 | ostree(QString(QStringLiteral("ostree static-delta apply-offline %1")).arg(packagePath), &ok);
374 | if (!ok) return false;
375 |
376 | g_autoptr(GVariant) toCsumV = g_variant_get_child_value (deltaSuperblock, 3);
377 | if (!ostree_validate_structureof_csum_v (toCsumV, &error)) {
378 | emitGError(error);
379 | return false;
380 | }
381 |
382 | g_autofree char *toCsum = ostree_checksum_from_bytes_v (toCsumV);
383 | *updateToRev = QString::fromLatin1(toCsum);
384 |
385 | QString remoteMetadata;
386 | ostree(QString(QStringLiteral("ostree reset qt-os:linux/qt %1")).arg(*updateToRev), &ok);
387 | if (ok) remoteMetadata = metadataFromRev(*updateToRev, &ok);
388 | if (ok) emit remoteMetadataChanged(*updateToRev, remoteMetadata);
389 | return ok;
390 | }
391 |
392 | void QOtaClientAsync::_updateRemoteMetadataOffline(const QString &packagePath)
393 | {
394 | QString rev;
395 | glnx_unref_object OstreeSysroot *sysroot = defaultSysroot();
396 | bool ok = sysroot && extractPackage(packagePath, sysroot, &rev);
397 | emit updateRemoteMetadataOfflineFinished(ok);
398 | }
399 |
400 | void QOtaClientAsync::_updateOffline(const QString &packagePath)
401 | {
402 | QString rev;
403 | glnx_unref_object OstreeSysroot *sysroot = defaultSysroot();
404 | bool ok = sysroot && extractPackage(packagePath, sysroot, &rev) &&
405 | deployCommit(rev, sysroot) && handleRevisionChanges(sysroot, true);
406 |
407 | emit updateOfflineFinished(ok);
408 | }
409 |
410 | QT_END_NAMESPACE
411 |
--------------------------------------------------------------------------------
/src/lib/qotaclientasync_p.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #ifndef QOTACLIENTASYNC_P_H
30 | #define QOTACLIENTASYNC_P_H
31 |
32 | #include "qotaclient.h"
33 | #include "qotaclient_p.h"
34 |
35 | #include
36 | #include
37 |
38 | QT_BEGIN_NAMESPACE
39 |
40 | struct OstreeSysroot;
41 | // from gerror.h
42 | typedef struct _GError GError;
43 |
44 | class QOtaClientPrivate;
45 |
46 | class QOtaClientAsync : public QObject
47 | {
48 | Q_OBJECT
49 | public:
50 | QOtaClientAsync();
51 | virtual ~QOtaClientAsync();
52 |
53 | QString ostree(const QString &command, bool *ok, bool updateStatus = false);
54 | bool refreshMetadata(QOtaClientPrivate *d = nullptr);
55 |
56 | signals:
57 | void fetchRemoteMetadata();
58 | void fetchRemoteMetadataFinished(bool success);
59 | void update(const QString &updateToRev);
60 | void updateFinished(bool success);
61 | void rollback();
62 | void rollbackFinished(bool success);
63 | void updateOffline(const QString &packagePath);
64 | void updateOfflineFinished(bool success);
65 | void updateRemoteMetadataOffline(const QString &packagePath);
66 | void updateRemoteMetadataOfflineFinished(bool success);
67 | void rollbackMetadataChanged(const QString &rollbackRev, const QString &rollbackMetadata, int treeCount);
68 | void errorOccurred(const QString &error);
69 | void statusStringChanged(const QString &status);
70 | void remoteMetadataChanged(const QString &remoteRev, const QString &remoteMetadata);
71 | void defaultRevisionChanged(const QString &defaultRevision, const QString &defaultMetadata);
72 |
73 | protected:
74 | OstreeSysroot* defaultSysroot();
75 | QString metadataFromRev(const QString &rev, bool *ok);
76 | int rollbackIndex(OstreeSysroot *sysroot);
77 | bool handleRevisionChanges(OstreeSysroot *sysroot, bool reloadSysroot = false);
78 | void emitGError(GError *error);
79 | bool deployCommit(const QString &commit, OstreeSysroot *sysroot);
80 | bool extractPackage(const QString &packagePath, OstreeSysroot *sysroot, QString *updateToRev);
81 |
82 | void _fetchRemoteMetadata();
83 | void _update(const QString &updateToRev);
84 | void _rollback();
85 | void _updateOffline(const QString &packagePath);
86 | void _updateRemoteMetadataOffline(const QString &packagePath);
87 | };
88 |
89 | QT_END_NAMESPACE
90 |
91 | #endif // QOTACLIENTASYNC_P_H
92 |
--------------------------------------------------------------------------------
/src/lib/qotarepositoryconfig.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #include "qotarepositoryconfig_p.h"
30 | #include "qotarepositoryconfig.h"
31 |
32 | #include
33 | #include
34 | #include
35 |
36 | QT_BEGIN_NAMESPACE
37 |
38 | const QString url(QStringLiteral("url="));
39 | const QString clientCert(QStringLiteral("tls-client-cert-path="));
40 | const QString clientKey(QStringLiteral("tls-client-key-path="));
41 | const QString ca(QStringLiteral("tls-ca-path="));
42 | // these 'bool' values by default are 'false' in QOtaRepositoryConfig
43 | const QString gpg(QStringLiteral("gpg-verify=true"));
44 | const QString tls(QStringLiteral("tls-permissive=true"));
45 |
46 | QOtaRepositoryConfigPrivate::QOtaRepositoryConfigPrivate(QOtaRepositoryConfig *repo) :
47 | q_ptr(repo),
48 | m_gpgVerify(false),
49 | m_tlsPermissive(false)
50 | {
51 | }
52 |
53 | QOtaRepositoryConfigPrivate::~QOtaRepositoryConfigPrivate()
54 | {
55 | }
56 |
57 | QOtaRepositoryConfig *QOtaRepositoryConfigPrivate::repositoryConfigFromFile(const QString &configPath) const
58 | {
59 | if (!QDir().exists(configPath))
60 | return nullptr;
61 |
62 | QFile config(configPath);
63 | if (!config.open(QFile::ReadOnly))
64 | return nullptr;
65 |
66 | QOtaRepositoryConfig *conf = new QOtaRepositoryConfig();
67 | QTextStream in(&config);
68 | while (!in.atEnd()) {
69 | QString line = in.readLine().trimmed();
70 | // string(s)
71 | if (line.startsWith(url))
72 | conf->setUrl(line.mid(url.length()));
73 | else if (line.startsWith(clientCert))
74 | conf->setTlsClientCertPath(line.mid(clientCert.length()));
75 | else if (line.startsWith(clientKey))
76 | conf->setTlsClientKeyPath(line.mid(clientKey.length()));
77 | else if (line.startsWith(ca))
78 | conf->setTlsCaPath(line.mid(ca.length()));
79 | // bool(s)
80 | else if (line.startsWith(gpg))
81 | conf->setGpgVerify(true);
82 | else if (line.startsWith(tls))
83 | conf->setTlsPermissive(true);
84 | }
85 |
86 | return conf;
87 | }
88 |
89 | /*!
90 | \inqmlmodule QtOtaUpdate
91 | \qmltype OtaRepositoryConfig
92 | \instantiates QOtaRepositoryConfig
93 | \brief Used to configure the OSTree repository.
94 |
95 | OtaRepositoryConfig
96 | \include qotarepositoryconfig.cpp repository-config-description
97 | */
98 |
99 | /*!
100 | \class QOtaRepositoryConfig
101 | \inmodule qtotaupdate
102 | \brief Used to configure the OSTree repository.
103 |
104 | QOtaRepositoryConfig
105 | //! [repository-config-description]
106 | provides an API to configure the OSTree repository (located in \c {/ostree/repo}). The
107 | update process synchronizes the local repository with the remote repository (see \l url).
108 | The local repository keeps history for the current and the previous snapshot of the system.
109 |
110 | This class is used to configure TLS authentication and whether to utilize GPG for update
111 | integrity verification.
112 |
113 | //! [repository-config-description]
114 | */
115 |
116 | /*!
117 | \qmlsignal OtaRepositoryConfig::urlChanged()
118 |
119 | This signal is emitted when the value of \l url changes.
120 | */
121 |
122 | /*!
123 | \fn void QOtaRepositoryConfig::urlChanged()
124 |
125 | This signal is emitted when the value of \l url changes.
126 | */
127 |
128 | /*!
129 | \qmlsignal OtaRepositoryConfig::gpgVerifyChanged()
130 |
131 | This signal is emitted when the value of gpgVerify changes.
132 | */
133 |
134 | /*!
135 | \fn void QOtaRepositoryConfig::gpgVerifyChanged()
136 |
137 | This signal is emitted when the value of gpgVerify changes.
138 | */
139 |
140 | /*!
141 | \qmlsignal OtaRepositoryConfig::tlsClientCertPathChanged()
142 |
143 | This signal is emitted when the value of tlsClientCertPath changes.
144 | */
145 |
146 | /*!
147 | \fn void QOtaRepositoryConfig::tlsClientCertPathChanged()
148 |
149 | This signal is emitted when the value of tlsClientCertPath changes.
150 | */
151 |
152 | /*!
153 | \qmlsignal OtaRepositoryConfig::tlsClientKeyPathChanged()
154 |
155 | This signal is emitted when the value of tlsClientKeyPath changes.
156 | */
157 |
158 | /*!
159 | \fn void QOtaRepositoryConfig::tlsClientKeyPathChanged()
160 |
161 | This signal is emitted when the value of tlsClientKeyPath changes.
162 | */
163 |
164 | /*!
165 | \qmlsignal OtaRepositoryConfig::tlsPermissiveChanged()
166 |
167 | This signal is emitted when the value of tlsPermissive changes.
168 | */
169 |
170 | /*!
171 | \fn void QOtaRepositoryConfig::tlsPermissiveChanged()
172 |
173 | This signal is emitted when the value of tlsPermissive changes.
174 | */
175 |
176 | /*!
177 | \qmlsignal OtaRepositoryConfig::tlsCaPathChanged()
178 |
179 | This signal is emitted when the value of tlsCaPath changes.
180 | */
181 |
182 | /*!
183 | \fn void QOtaRepositoryConfig::tlsCaPathChanged()
184 |
185 | This signal is emitted when the value of tlsCaPath changes.
186 | */
187 |
188 | QOtaRepositoryConfig::QOtaRepositoryConfig(QObject *parent) :
189 | QObject(parent),
190 | d_ptr(new QOtaRepositoryConfigPrivate(this))
191 | {
192 | }
193 |
194 | QOtaRepositoryConfig::~QOtaRepositoryConfig()
195 | {
196 | delete d_ptr;
197 | }
198 |
199 | void QOtaRepositoryConfig::setUrl(const QString &url)
200 | {
201 | Q_D(QOtaRepositoryConfig);
202 | if (url.trimmed() == d->m_url)
203 | return;
204 |
205 | d->m_url = url.trimmed();
206 | emit urlChanged();
207 | }
208 |
209 | /*!
210 | \qmlproperty string OtaRepositoryConfig::url
211 |
212 | Holds a URL to a remote OSTree repository.
213 | The supported schemes are \c http and \c https.
214 | */
215 |
216 | /*!
217 | \property QOtaRepositoryConfig::url
218 |
219 | Holds a URL to a remote OSTree repository.
220 | The supported schemes are \c http and \c https.
221 | */
222 | QString QOtaRepositoryConfig::url() const
223 | {
224 | return d_func()->m_url;
225 | }
226 |
227 | void QOtaRepositoryConfig::setGpgVerify(bool verify)
228 | {
229 | Q_D(QOtaRepositoryConfig);
230 | if (verify == d->m_gpgVerify)
231 | return;
232 |
233 | d->m_gpgVerify = verify;
234 | emit gpgVerifyChanged();
235 | }
236 |
237 | /*!
238 | \qmlproperty bool OtaRepositoryConfig::gpgVerify
239 |
240 | Holds whether OSTree will require system updates to be signed by a known GPG key.
241 |
242 | Default is \c false.
243 | */
244 |
245 | /*!
246 | \property QOtaRepositoryConfig::gpgVerify
247 |
248 | Holds whether OSTree will require system updates to be signed by a known GPG key.
249 |
250 | Default is \c false.
251 | */
252 | bool QOtaRepositoryConfig::gpgVerify() const
253 | {
254 | return d_func()->m_gpgVerify;
255 | }
256 |
257 | void QOtaRepositoryConfig::setTlsClientCertPath(const QString &certPath)
258 | {
259 | Q_D(QOtaRepositoryConfig);
260 | if (certPath.trimmed() == d->m_clientCertPath)
261 | return;
262 |
263 | d->m_clientCertPath = certPath.trimmed();
264 | emit tlsClientCertPathChanged();
265 | }
266 |
267 | /*!
268 | \qmlproperty string OtaRepositoryConfig::tlsClientCertPath
269 |
270 | Holds a path to a client-side certificate, to present when making requests
271 | to the remote server.
272 | */
273 |
274 | /*!
275 | \property QOtaRepositoryConfig::tlsClientCertPath
276 |
277 | Holds a path to a client-side certificate, to present when making requests
278 | to the remote server.
279 | */
280 | QString QOtaRepositoryConfig::tlsClientCertPath() const
281 | {
282 | return d_func()->m_clientCertPath;
283 | }
284 |
285 | void QOtaRepositoryConfig::setTlsClientKeyPath(const QString &keyPath)
286 | {
287 | Q_D(QOtaRepositoryConfig);
288 | if (keyPath.trimmed() == d->m_clientKeyPath)
289 | return;
290 |
291 | d->m_clientKeyPath = keyPath.trimmed();
292 | emit tlsClientKeyPathChanged();
293 | }
294 |
295 | /*!
296 | \qmlproperty string OtaRepositoryConfig::tlsClientKeyPath
297 |
298 | Holds a path to a client-side certificate's key, to present when making requests
299 | to the remote server.
300 | */
301 |
302 | /*!
303 | \property QOtaRepositoryConfig::tlsClientKeyPath
304 |
305 | Holds a path to a client-side certificate's key, to present when making requests
306 | to the remote server.
307 | */
308 | QString QOtaRepositoryConfig::tlsClientKeyPath() const
309 | {
310 | return d_func()->m_clientKeyPath;
311 | }
312 |
313 | void QOtaRepositoryConfig::setTlsPermissive(bool permissive)
314 | {
315 | Q_D(QOtaRepositoryConfig);
316 | if (permissive == d->m_tlsPermissive)
317 | return;
318 |
319 | d->m_tlsPermissive = permissive;
320 | emit tlsPermissiveChanged();
321 | }
322 |
323 | /*!
324 | \qmlproperty bool OtaRepositoryConfig::tlsPermissive
325 |
326 | Holds whether to check the server's TLS certificates against the system's certificate store.
327 |
328 | Default is \c false.
329 | */
330 |
331 | /*!
332 | \property QOtaRepositoryConfig::tlsPermissive
333 |
334 | Holds whether to check the server's TLS certificates against the system's certificate store.
335 |
336 | Default is \c false.
337 | */
338 | bool QOtaRepositoryConfig::tlsPermissive() const
339 | {
340 | return d_func()->m_tlsPermissive;
341 | }
342 |
343 | void QOtaRepositoryConfig::setTlsCaPath(const QString &caPath)
344 | {
345 | Q_D(QOtaRepositoryConfig);
346 | if (caPath.trimmed() == d->m_tlsCaPath)
347 | return;
348 |
349 | d->m_tlsCaPath = caPath.trimmed();
350 | emit tlsCaPathChanged();
351 | }
352 |
353 | /*!
354 | \qmlproperty string OtaRepositoryConfig::tlsCaPath
355 |
356 | Holds a path to a file containing trusted anchors instead of the system's CA database.
357 | */
358 |
359 | /*!
360 | \property QOtaRepositoryConfig::tlsCaPath
361 |
362 | Holds a path to a file containing trusted anchors instead of the system's CA database.
363 | */
364 | QString QOtaRepositoryConfig::tlsCaPath() const
365 | {
366 | return d_func()->m_tlsCaPath;
367 | }
368 |
369 | QT_END_NAMESPACE
370 |
--------------------------------------------------------------------------------
/src/lib/qotarepositoryconfig.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #ifndef QOTAREPOSITORYCONFIG_H
30 | #define QOTAREPOSITORYCONFIG_H
31 |
32 | #include
33 | #include
34 |
35 | QT_BEGIN_NAMESPACE
36 |
37 | class QOtaClient;
38 | class QOtaRepositoryConfigPrivate;
39 |
40 | class Q_DECL_EXPORT QOtaRepositoryConfig : public QObject
41 | {
42 | Q_OBJECT
43 | Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
44 | Q_PROPERTY(bool gpgVerify READ gpgVerify WRITE setGpgVerify NOTIFY gpgVerifyChanged)
45 | Q_PROPERTY(QString tlsClientCertPath READ tlsClientCertPath WRITE setTlsClientCertPath NOTIFY tlsClientCertPathChanged)
46 | Q_PROPERTY(QString tlsClientKeyPath READ tlsClientKeyPath WRITE setTlsClientKeyPath NOTIFY tlsClientKeyPathChanged)
47 | Q_PROPERTY(bool tlsPermissive READ tlsPermissive WRITE setTlsPermissive NOTIFY tlsPermissiveChanged)
48 | Q_PROPERTY(QString tlsCaPath READ tlsCaPath WRITE setTlsCaPath NOTIFY tlsCaPathChanged)
49 | public:
50 | explicit QOtaRepositoryConfig(QObject *parent = nullptr);
51 | virtual ~QOtaRepositoryConfig();
52 |
53 | void setUrl(const QString &url);
54 | QString url() const;
55 |
56 | void setGpgVerify(bool verify);
57 | bool gpgVerify() const;
58 |
59 | void setTlsClientCertPath(const QString &certPath);
60 | QString tlsClientCertPath() const;
61 |
62 | void setTlsClientKeyPath(const QString &keyPath);
63 | QString tlsClientKeyPath() const;
64 |
65 | void setTlsPermissive(bool permissive);
66 | bool tlsPermissive() const;
67 |
68 | void setTlsCaPath(const QString &caPath);
69 | QString tlsCaPath() const;
70 |
71 | Q_SIGNALS:
72 | void urlChanged();
73 | void gpgVerifyChanged();
74 | void tlsClientCertPathChanged();
75 | void tlsClientKeyPathChanged();
76 | void tlsPermissiveChanged();
77 | void tlsCaPathChanged();
78 |
79 | private:
80 | Q_DISABLE_COPY(QOtaRepositoryConfig)
81 | Q_DECLARE_PRIVATE(QOtaRepositoryConfig)
82 | QOtaRepositoryConfigPrivate *const d_ptr;
83 | friend class QOtaClient;
84 | };
85 |
86 | QT_END_NAMESPACE
87 |
88 | #endif // QOTAREPOSITORYCONFIG_H
89 |
--------------------------------------------------------------------------------
/src/lib/qotarepositoryconfig_p.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 The Qt Company Ltd.
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the Qt OTA Update module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:GPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU
19 | ** General Public License version 3 or (at your option) any later version
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 | ** included in the packaging of this file. Please review the following
23 | ** information to ensure the GNU General Public License requirements will
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 | **
26 | ** $QT_END_LICENSE$
27 | **
28 | ****************************************************************************/
29 | #ifndef QOTAREPOSITORYCONFIG_P_H
30 | #define QOTAREPOSITORYCONFIG_P_H
31 |
32 | #endif // QOTAREPOSITORYCONFIG_P_H
33 |
34 | #include
35 | #include
36 |
37 | QT_BEGIN_NAMESPACE
38 |
39 | class QOtaRepositoryConfig;
40 |
41 | class QOtaRepositoryConfigPrivate : public QObject
42 | {
43 | Q_OBJECT
44 | Q_DECLARE_PUBLIC(QOtaRepositoryConfig)
45 | public:
46 | QOtaRepositoryConfigPrivate(QOtaRepositoryConfig *repo);
47 | virtual ~QOtaRepositoryConfigPrivate();
48 |
49 | QOtaRepositoryConfig *repositoryConfigFromFile(const QString &configPath) const;
50 |
51 | // members
52 | QOtaRepositoryConfig *const q_ptr;
53 | QString m_url;
54 | bool m_gpgVerify;
55 | QString m_clientCertPath;
56 | QString m_clientKeyPath;
57 | bool m_tlsPermissive;
58 | QString m_tlsCaPath;
59 | };
60 |
61 | QT_END_NAMESPACE
62 |
--------------------------------------------------------------------------------
/src/src.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 | CONFIG += ordered
3 | SUBDIRS += \
4 | lib \
5 | imports
6 |
--------------------------------------------------------------------------------
/sync.profile:
--------------------------------------------------------------------------------
1 | %modules = ( # path to module name map
2 | "QtOtaUpdate" => "$basedir/src/lib"
3 | );
4 | %moduleheaders = ( # restrict the module headers to those found in relative path
5 | );
6 | # Module dependencies.
7 | # Every module that is required to build this module should have one entry.
8 | # Each of the module version specifiers can take one of the following values:
9 | # - A specific Git revision.
10 | # - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch)
11 | #
12 | %dependencies = (
13 | "qtbase" => ""
14 | );
15 |
--------------------------------------------------------------------------------