├── .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 |
2 |
3 | 4 |

Reference

5 |
6 | 10 |
11 |
12 |
13 | 14 | 15 |
16 | 20 |
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 | --------------------------------------------------------------------------------