aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-core
diff options
context:
space:
mode:
authorJoshua Lock <joshua.g.lock@intel.com>2016-02-25 21:13:00 +0000
committerJoshua Lock <joshua.g.lock@intel.com>2016-02-25 21:28:43 +0000
commitaf788a23eece3405e171bcd5ba247db49ad9ef62 (patch)
treecc3afb6a1533cb2edb180e78c46f78ba8af3b7fb /recipes-core
downloadmeta-swupd-af788a23eece3405e171bcd5ba247db49ad9ef62.tar.gz
meta-swupd-af788a23eece3405e171bcd5ba247db49ad9ef62.tar.bz2
meta-swupd-af788a23eece3405e171bcd5ba247db49ad9ef62.zip
Initial population
This initial layer version provides an initial set of metadata to enable integration of the swupd (https://clearlinux.org/features/software-update) software updater into an image. Approach: An image that inherits the swupd-image bbclass will automatically have bundle 'chroots' created which contain the filesystem contents of the specified bundles, with the contents of the inheriting image forming the default os-core bundle. The mechanism to achieve this is that several virtual image recipes are created using the swupdbundle class, one for each defined bundle plus a 'mega' image recipe. The 'mega' image contains the base image plus the contents of all of the bundles, whilst bundle images contain only the base image plus the contents of a single bundle. We build the mega image first, then the base image (the one which inherits this class) and finally all of the bundle images. Each non-mega image has a manifest generated that lists the file contents of the image. We took the approach of building images, rather than populating the chroot-like bundle directories with a package manager, because various layers and recipes make changes to the rootfs contents outside of the package manager, particularly with IMAGE_POSTPROCESS_COMMAND, etc. Once the images and their manifests have been created each bundle image manifest is compared to the base image manifest in order to generate a list of files in the bundle image which don't exist in the base image. Files in this list are then preserved in the bundle directory for processing by swupd-server in order to generate update artefacts. Finally the binaries from swupd-server are called on the bundle directories to generate the artefacts for consumption by a swupd client. How to: * inherit the swupd-image class in your core OS image. swupd-based OS's use bundles, the primary one of which, os-core, is defined as the contents of this image. * Assign a list of names for bundles you wish to generate to the SWUPD_BUNDLES variable i.e. SWUPD_BUNDLES = "feature_one feature_two" * Assign a list of packages for which their content should be included in a bundle to a varflag of BUNDLE_CONTENTS which matches the bundle name i.e. BUNDLE_CONTENTS[feature_one] = "package_one package_three package_six" * Ensure the OS_VERSION variable is assigned an integer value and increased before each image build which should generate swupd update artefacts. This variable must echo the same version number as is used to set the VERSION_ID field of os-release as swupd-client will use it to check for updates. * Publish the contents of ${DEPLOY_DIR}/swupd/${MACHINE}/${IMAGE_BASENAME}/www on a server for consumption by swupd-client * Use swupd client sub-commands with the -u argument pointing to the contents published above Known issues: * shared pseudo database: the bundle chroot-like directories are generated per-recipe and processed by a task of the inheriting recipe. In order for the files generated outside of the base recipe to have correct permissions when processed by swupd-server we need to share a pseudo database across the recipes. This database is currently never cleaned up, which is likely to cause headaches due to the way pseudo operates on inodes that could be reused outside of pseudo's influence. We have yet to determine an appropriate time to perform housekeeping on this database (we essentially need the database to be removed when DEPLOY_DIR_SWUPD is removed). * oe-swupd-helpers: this recipe provides stub implementations only of some swupd-client helpers. Anyone wishing to utilise swupd in a deployed image will need to at least override kernel_updater.sh and systemdboot_updater.sh. * hard-coded paths: swupd assumes Clear Linux as a host OS and hard-codes several paths to directories, programs and configuration files on Clear. * builds a lot of images: due to the approach taken there are n+2 images built, where n is the number of bundles defined in SWUPD_BUNDLES. * creates a lot of duplicate files: due to the way swupd works by processing chroot-like bundle directories on each os release we potentially end up carrying a lot of duplicate files in DEPLOY_DIR_SWUPD. We intend to look at using the hardlink program to replace duplicate files in that directory with hard links in order to save disk space. * requires far more testing: there are a lot of combinations of bundle contents, rootfs modification commands (IMAGE_POSTPROCESS_COMMAND, IMAGE_PREPROCESS_COMMAND, etc) that can affect the inputs to swupd and our testing has likely missed areas of issue. * OS_VERSION: introduces a new variable for the OS version number when we already have a DISTRO_VERSION variable. This was done because swupd makes various assumptions about the version number which aren't necessarily true for traditional DISTRO_VERSION values in OE et al. Co-authored-by: Mariano Lopez <mariano.lopez@linux.intel.com> Signed-off-by: Joshua Lock <joshua.g.lock@intel.com>
Diffstat (limited to 'recipes-core')
-rw-r--r--recipes-core/swupd-client/oe-swupd-helpers.bb39
-rw-r--r--recipes-core/swupd-client/oe-swupd-helpers/catalog-trigger.service8
-rwxr-xr-xrecipes-core/swupd-client/oe-swupd-helpers/clr_pre_update.sh16
-rwxr-xr-xrecipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh9
-rwxr-xr-xrecipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh~395
-rw-r--r--recipes-core/swupd-client/oe-swupd-helpers/ldconfig-trigger.service8
-rwxr-xr-xrecipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh10
-rwxr-xr-xrecipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh~267
-rw-r--r--recipes-core/swupd-client/oe-swupd-helpers/tmpfiles-trigger.service8
-rw-r--r--recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target5
-rw-r--r--recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target~9
-rw-r--r--recipes-core/swupd-client/swupd-client/0001-Tolerate-quotes-in-os-release-files.patch59
-rw-r--r--recipes-core/swupd-client/swupd-client/Change-systemctl-path-to-OE-systemctl-path.patch31
-rw-r--r--recipes-core/swupd-client/swupd-client/Fix-build-failure-on-Yocto.patch36
-rw-r--r--recipes-core/swupd-client/swupd-client/Right-usage-of-AC_ARG_ENABLE-on-bzip2.patch38
-rw-r--r--recipes-core/swupd-client/swupd-client_2.87.bb49
-rw-r--r--recipes-core/swupd-server/swupd-server/0001-Add-option-S-to-take-the-state-data-dir-as-an-argume.patch782
-rw-r--r--recipes-core/swupd-server/swupd-server/0001-Add-system_argv-helper-for-safer-calls-to-system-uti.patch133
-rw-r--r--recipes-core/swupd-server/swupd-server/0002-Add-configure-option-to-re-enable-config-files-in-ma.patch66
-rw-r--r--recipes-core/swupd-server/swupd-server/0002-Fix-regression-that-introduced-a-directory-named.patch29
-rw-r--r--recipes-core/swupd-server/swupd-server_2.53.bb33
21 files changed, 2030 insertions, 0 deletions
diff --git a/recipes-core/swupd-client/oe-swupd-helpers.bb b/recipes-core/swupd-client/oe-swupd-helpers.bb
new file mode 100644
index 0000000..4690f21
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers.bb
@@ -0,0 +1,39 @@
+SUMMARY = "OE swupd helper files"
+DESCRIPTION = "swupd-client assumes the presence of various helpers, this is a minimal OE \
+implementation of the required scripts and systemd units. \
+Scripts are modified versions of those in clr-specialized-updaters and units are modified \
+versions of those in clr-systemd-config"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
+
+SRC_URI = "file://update-triggers.target \
+ file://catalog-trigger.service \
+ file://ldconfig-trigger.service \
+ file://tmpfiles-trigger.service \
+ file://clr_pre_update.sh \
+ file://kernel_updater.sh \
+ file://systemdboot_updater.sh \
+ "
+
+inherit allarch distro_features_check systemd
+
+REQUIRED_DISTRO_FEATURES = "systemd"
+
+do_install () {
+ install -d ${D}${systemd_system_unitdir}
+ for svc in `find ${WORKDIR} -maxdepth 1 -name *.target -o -name *.service`; do
+ install -m 0644 $svc ${D}${systemd_system_unitdir}/
+ sed -i -e s#/bin#${base_bindir}# ${D}${systemd_system_unitdir}/`basename $svc`
+ sed -i -e s#/sbin#${base_sbindir}# ${D}${systemd_system_unitdir}/`basename $svc`
+ sed -i -e s#/lib#${base_libdir}# ${D}${systemd_system_unitdir}/`basename $svc`
+ done
+
+ # NOTE: swupd-client hard-codes /usr/bin
+ install -d ${D}/usr/bin
+ for helper in `find ${WORKDIR} -maxdepth 1 -name *.sh`; do
+ install $helper ${D}/usr/bin/
+ done
+}
+
+RDEPENDS_${PN} += "bash"
+FILES_${PN} += "${systemd_system_unitdir}" \ No newline at end of file
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/catalog-trigger.service b/recipes-core/swupd-client/oe-swupd-helpers/catalog-trigger.service
new file mode 100644
index 0000000..8266174
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/catalog-trigger.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Rebuild journal catalog
+Documentation=man:journalctl(1)
+BindsTo=update-triggers.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/journalctl --update-catalog
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/clr_pre_update.sh b/recipes-core/swupd-client/oe-swupd-helpers/clr_pre_update.sh
new file mode 100755
index 0000000..6302090
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/clr_pre_update.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+set -u
+
+#######################################
+# Clear Linux General Purpose Pre-update helper script
+# This is a script meant to update state information external to what swupd updates.
+# The specific content of this script will vary over time, according to the state
+# change needed
+#######################################
+
+UPDATE_NAME="Pre-update"
+VERSION="3.60"
+
+### Enter your script here
+echo "Running script '$UPDATE_NAME'"
+exit 0
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh b/recipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh
new file mode 100755
index 0000000..b0d5335
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+#######################################
+# This script will be called after an updated kernel has been installed
+#######################################
+
+### Enter your script here
+echo "Running script 'kernel_updater.sh'"
+exit 0
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh~ b/recipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh~
new file mode 100755
index 0000000..0060be0
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/kernel_updater.sh~
@@ -0,0 +1,395 @@
+#!/bin/bash
+
+#######################################
+# Clear Linux Kernel update script
+# Author Brad Peters <brad.t.peters@intel.com>
+# kernel_updater.sh: Provides a minimal-copy wrapper which finesses
+# the VFAT /boot fs to minimize writes, while implementing Clear Linux
+# update/install and fix policies
+#######################################
+
+VERSION=3.60
+HASH="/usr/bin/sha256sum"
+
+function printUsage()
+{
+ echo "kernel_updater.sh [--path <path to chroot>]"
+ echo " -v | --version : Prints version of kernel_updater.sh"
+ echo " -h | --help : Prints usage"
+ echo " -p | --path : Optional. Used when creating new image "
+ echo " in a subdir. Should be absolute path"
+}
+
+function cleanDuplicates()
+{
+ # Move duplicate kernels out of the way in /boot, if necessary.
+ # Kernels in /usr are considered 'correct'
+ SRCFILES=org.clearlinux.*
+
+ for f in $SRCFILES
+ do
+ destfile="$SUBDIR/boot/$f"
+ if [ ! -f $destfile ]
+ then
+ continue
+ fi
+
+ srcsha=$(sha1sum $f)
+ destsha=$(sha1sum $destfile)
+
+ srcsha=$(echo $srcsha | awk '{print $1}')
+ destsha=$(echo $destsha | awk '{print $1}')
+
+ if [ "$srcsha" != "$destsha" ]
+ then
+ newname="${destfile/clearlinux/clearTMP}"
+# echo "Renaming $destfile to $newname prior to replacement"
+ mv $destfile $newname
+ fi
+ done
+}
+
+function cleanDuplicatesEFI()
+{
+ # Move duplicate kernels out of the way in /boot, if necessary.
+ # Kernels in /usr are considered 'correct'
+ SRCFILES=*.efi
+
+ for f in $SRCFILES
+ do
+ destfile="$SUBDIR/boot/EFI/Linux/$f"
+ if [ ! -f $destfile ]
+ then
+ continue
+ fi
+
+ srcsha=$(sha1sum $f)
+ destsha=$(sha1sum $destfile)
+
+ srcsha=$(echo $srcsha | awk '{print $1}')
+ destsha=$(echo $destsha | awk '{print $1}')
+
+ if [ "$srcsha" != "$destsha" ]
+ then
+ newname="${destfile/clearlinux/clearTMP}"
+# echo "Renaming $destfile to $newname prior to replacement"
+ mv $destfile $newname
+ fi
+ done
+}
+
+
+function cleanOldEFI()
+{
+ # Clean up old kernels
+
+ # Get list of all kernels in reverse sorted order (based only on release ID)
+ LS_KERNELS_SORTED=$(ls org.clearlinux*.efi | sort --field-separator=- -n -k3 -r)
+
+ i=0
+ for k in $LS_KERNELS_SORTED
+ do
+ if [[ "$i" -ge 3 ]]
+ then
+ cur_kernel=$(uname -r | cut -f1,2,3 -d.)
+ ls_kernel=$(echo $k | cut -f4,5,6 -d.)
+
+ #Do not clean up currently running kernel
+ if [[ $ls_kernel = $cur_kernel ]]
+ then
+ echo "Keeping $k. Is currently running kernel"
+ continue;
+ fi
+
+ echo "Clean up old kernel: $k"
+ echo "Clean up modules $SUBDIR/lib/modules/$ls_kernel"
+ rm "$k"
+ rm -Rf "$SUBDIR/lib/modules/$ls_kernel"
+ fi
+ i=$((i + 1))
+ done
+}
+
+function cleanOld()
+{
+ # Clean up old kernels
+
+ # Get list of all kernels in reverse sorted order (based only on release ID)
+ LS_KERNELS_SORTED=($(ls org.clearlinux.* | sort --field-separator=- -n -k3 -r))
+
+ i=0
+ for k in $LS_KERNELS_SORTED[*]
+ do
+ if [[ "$i" -ge 3 ]]
+ then
+ cur_kernel=$(uname -r | cut -f1,2,3 -d.)
+ ls_kernel=$(echo $k | cut -f4,5,6 -d.)
+
+ #Do not clean up currently running kernel
+ if [[ $ls_kernel = $cur_kernel ]]
+ then
+ echo "Keeping $k. Is currently running kernel"
+ continue;
+ fi
+
+ echo "Clean up old kernel: $k"
+ if [[ -z "${SUBDIR}" ]]; then
+ ${KINSTALL} remove "${ls_kernel}"
+ else
+ ${KINSTALL} --root "${SUBDIR}" remove "${ls_kernel}"
+ fi
+ fi
+ i=$((i + 1))
+ done
+}
+
+#Set the "default" entry option in file /boot/loader/loader.conf
+#requires /boot partition mounted
+function updateDefaultEntry()
+{
+ LOADER_CONF_FILE="/boot/loader/loader.conf"
+ if [ ! -f "$SUBDIR""$LOADER_CONF_FILE" ]; then
+ mkdir -p "$(dirname "$SUBDIR""$LOADER_CONF_FILE" )"
+ touch "$SUBDIR""$LOADER_CONF_FILE"
+ fi
+
+ for typename in "native" "kvm" "container" ; do
+ TPATH="${SUBDIR}/usr/lib/kernel/default-${typename}"
+ if [[ -e "${TPATH}" ]] ; then
+ kfile="$(basename $(readlink ${TPATH}))"
+
+ ktype="$(echo $kfile|cut -d. -f 3)"
+ kversion="$(echo $kfile|cut -f4,5,6 -d.|cut -d - -f 1)"
+ krelease="$(echo $kfile|cut -d - -f 2)"
+ defaultConf="Clear-linux-${ktype}-${kversion}-${krelease}"
+ break
+ fi
+ done
+
+ if [[ -z "${defaultConf}" ]]; then
+ echo "Default loader cannot be found"
+ return
+ fi
+
+ configFile="$SUBDIR""$LOADER_CONF_FILE"
+ hashFile="${configFile}.sha256sum"
+
+ sync
+ echo "default ${defaultConf}" > "${configFile}"
+ sync
+
+ echo "Updated default to: ${defaultConf}"
+
+ if [[ -e "${hashFile}" ]]; then
+ echo "Removing legacy file: ${hashFile}"
+ rm -v "${hashFile}"
+ sync
+ fi
+}
+
+# Copies kernel from /usr to /boot. Checks each kernel to
+# ensure modules are installed, and only copies if so
+function copyKernelBlobs()
+{
+ pushd "$SUBDIR/usr/lib/kernel" > /dev/null
+ LS_KERNELS_SORTED=$(ls org.clearlinux.* | sort --field-separator=- -n -k3 -r)
+ popd > /dev/null
+
+ for k in $LS_KERNELS_SORTED
+ do
+ kernel_version=$(echo "$k" | cut -f4,5,6 -d.)
+ kernel_file="${SUBDIR}/usr/lib/kernel/$(basename ${k})"
+ kernel_blob=$(echo "$k" | cut -f5 -d/)
+ echo "Kernel version = $kernel_version , blob = $kernel_blob"
+ kmods=$(ls $SUBDIR/lib/modules | sed -n "/$kernel_version/p")
+ kmods_len=${#kmods}
+
+ # Proceed with cp if kernel modules are present and kernel is not
+ # already installed
+ if [ $kmods_len -gt 0 ] && [ ! -e "$SUBDIR/boot/$kernel_blob" ]
+ then
+ echo " Installing $k to $SUBDIR/boot/"
+ if [[ -z "${SUBDIR}" ]]; then
+ ${KINSTALL} add "${kernel_version}" "${kernel_file}"
+ else
+ ${KINSTALL} --root "${SUBDIR}" add "${kernel_version}" "${kernel_file}"
+ fi
+
+ if [[ $? -eq 0 ]]; then
+ echo "Installed $k to /boot/"
+ else
+ echo "Failed to copy $k to /boot"
+ fi
+
+ else
+ if [ ! $kmods_len -gt 0 ]
+ then
+ echo "Skipping kernel $kernel_version - No modules found."
+ fi
+ if [ -e "$SUBDIR/boot/$kernel_blob" ]
+ then
+ echo "Skipping kernel $kernel_version - Already installed."
+ fi
+ fi
+
+ done
+}
+
+##################### Main #####################
+if [[ $# > 2 ]]
+then
+ printUsage
+fi
+
+# Parse cmdline
+if [[ $# > 0 ]]
+then
+ case "$1" in
+ -h|--help)
+ printUsage
+ exit 0
+ ;;
+ -v|--version)
+ echo "kernel_updater.sh version $VERSION"
+ exit 0
+ ;;
+ -p|--path)
+ if [[ $# = 1 ]]
+ then
+ echo "Invalid arguments. --path requires a second argument."
+ exit 1
+ fi
+ SUBDIR="$2"
+ ;;
+ *)
+ echo "Unrecognized argument ($1). Try again"
+ printUsage
+ exit 1
+ ;;
+ esac
+fi
+
+if [[ $EUID -ne 0 ]]; then
+ echo "Must be root to execute kernel_updater.sh"
+ exit 1
+fi
+
+KINSTALL="${SUBDIR}/usr/bin/kernel-install"
+
+# Clean up /usr/lib/kernel to the minimal 3 kernels we want to install
+pushd "$SUBDIR/usr/lib/kernel/" > /dev/null
+
+have_blobs=0
+have_legacy=0
+for test in org.clearlinux.*; do
+ have_blobs=1
+ if [[ "${test}" == *.efi ]] ; then
+ have_legacy=1
+ fi
+ break;
+done
+if [[ "$have_blobs" -eq 1 ]]; then
+ cleanOld
+ if [[ "$have_legacy" -eq 1 ]]; then
+ cleanOldEFI
+ fi
+else
+ echo "No kernel blobs found."
+ echo "kernel update complete."
+ exit 0
+fi
+
+# Note that if we are given a --SUBDIR or if there are no org.clearlinux blobs
+# in /usr/lib/kernel, we do not attempt a mount of /boot
+if [ -z "${SUBDIR}" ]; then
+ # Get boot partition UUID
+ BOOTUUIDENTRY=$(ls /sys/firmware/efi/efivars/LoaderDevicePartUUID*)
+ if [[ -e $BOOTUUIDENTRY ]]; then
+ UUID=$(cat $BOOTUUIDENTRY | tr -cd '[[:alnum:]]._-' | tr '[:upper:]' '[:lower:]')
+ fi
+
+ strlen=${#UUID}
+
+ IS_MOUNTED=$(mount | sed -n '/boot/p' | sed -n '/vfat/p')
+ if [[ -z "$IS_MOUNTED" ]];
+ then
+ if [ "$strlen" -gt 0 ]
+ then
+ echo "Mounting boot partition with UUID=$UUID"
+ mount /dev/disk/by-partuuid/$UUID /boot -o rw -t vfat
+ else
+ echo "Failed to identify boot partition using /sys/firmware/efi/efivars."
+ echo " Attempting to mount by-partlabel"
+ mount /dev/disk/by-partlabel/ESP /boot -o rw -t vfat
+ fi
+ else
+ if [ "$strlen" -gt 0 ]
+ then
+ echo "Mounting boot partition with UUID=$UUID"
+ mount -o remount,rw /dev/disk/by-partuuid/$UUID /boot -t vfat
+ else
+ echo "Failed to identify boot partition using /sys/firmware/efi/efivars."
+ echo " Attempting to remount by-partlabel"
+ mount -o remount,rw /dev/disk/by-partlabel/ESP /boot -t vfat
+ fi
+ fi
+
+ IS_MOUNTED=$(mount | sed -n '/boot/p' | sed -n '/vfat/p')
+ if [[ -z "$IS_MOUNTED" ]];
+ then
+ echo "/boot partition was not found. Unable to continue"
+ exit 1
+ fi
+fi
+
+echo "Updating kernel... please wait..."
+# Identify kernels in /boot/EFI/Linux which are different from those
+# in /usr. Assume /usr is 'correct', and remove those in /boot
+cleanDuplicates
+cleanDuplicatesEFI
+
+# Sync after cleanup to protect against VFAT corruption
+pushd "$SUBDIR/boot" > /dev/null
+sync
+popd > /dev/null
+
+# Copy new kernels from /usr/lib/kernel into /boot/
+copyKernelBlobs
+
+# Step 3 - Cleanup kernels which were repaired, then sync
+if [[ -e "$SUBDIR/boot/EFI/Linux/" ]]; then
+ pushd "$SUBDIR/boot/EFI/Linux/" > /dev/null
+ rm -f *clearTMP* 2>/dev/null
+ sync
+ popd > /dev/null
+fi
+
+pushd "$SUBDIR/boot/" > /dev/null
+rm -f *clearTMP* 2>/dev/null
+sync
+popd > /dev/null
+
+# Clean up legacy EFI blobs which are no longer needed
+if [[ -e "$SUBDIR/boot/EFI/Linux" ]]; then
+ pushd "$SUBDIR/boot/EFI/Linux" > /dev/null
+ cleanOldEFI
+ sync
+ popd > /dev/null
+fi
+
+# Finally, clean up kernels which are no longer needed
+pushd "$SUBDIR/boot" > /dev/null
+cleanOld
+sync
+popd > /dev/null
+
+#Update default kernel with the latest version
+updateDefaultEntry
+
+if [ -z "${SUBDIR}" ]; then
+ umount /boot
+fi
+
+popd > /dev/null
+
+echo "kernel update complete."
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/ldconfig-trigger.service b/recipes-core/swupd-client/oe-swupd-helpers/ldconfig-trigger.service
new file mode 100644
index 0000000..224ac7a
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/ldconfig-trigger.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Rebuild Dynamic Linker Cache
+Documentation=man:ldconfig(8)
+BindsTo=update-triggers.target
+
+[Service]
+Type=oneshot
+ExecStart=/sbin/ldconfig -X
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh b/recipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh
new file mode 100755
index 0000000..470cd6e
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+#######################################
+# systemd bootloader installer
+# This script will be called after an updated bootloader has been installed
+#######################################
+
+### Enter your script here
+echo "Running script 'systemdboot_updater.sh'"
+exit 0
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh~ b/recipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh~
new file mode 100755
index 0000000..d8cdc7e
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/systemdboot_updater.sh~
@@ -0,0 +1,267 @@
+#!/bin/bash
+
+#######################################
+# Clear Linux systemd bootloader installer
+# Author Brad Peters <brad.t.peters@intel.com>
+# systemdboot_updater.sh provides a minimal-copy wrapper which finesses
+# the VFAT /boot fs to minimize writes, while implementing Clear Linux
+# update/install and fix policies
+#######################################
+
+
+VERSION=3.60
+
+mountedBoot=0
+
+function printUsage()
+{
+ echo "systemdboot_updater.sh [--path <path to chroot>]"
+ echo " -v | --version : Prints version of bootloader_updater.sh"
+ echo " -h | --help : Prints usage"
+ echo " -p | --path : Optional. Used when creating new image "
+ echo " in a subdir. Should be absolute path"
+}
+
+# Compares the sha1_sum of two files
+# Sets $sha1_cmp_res to 0 if equal, 1 if not
+function sha1_cmp()
+{
+ src=$1
+ dest=$2
+
+ if [[ ! -e $src ]]; then
+ echo "In sha1_cmp - $src File not found"
+ exit 1
+ fi
+
+ if [[ ! -e $dest ]]; then
+ # dest file does not exist, therefore we know the
+ # sha1_sum is different
+ sha1_cmp_res=1
+ return 1
+ fi
+
+ srcsha=$(sha1sum $src)
+ destsha=$(sha1sum $dest)
+
+ srcsha=$(echo $srcsha | awk '{print $1}')
+ destsha=$(echo $destsha | awk '{print $1}')
+
+ if [ $srcsha == $destsha ]
+ then
+ sha1_cmp_res=0
+ else
+ sha1_cmp_res=1
+ fi
+}
+
+
+
+#Checks if there is enough space to copy 2 files to /boot
+# Sets enoughSpace_res to 0 if not enough space to do the copy,
+# 1 if can saftely proceed
+function enoughSpace()
+{
+ src1=$1
+ src2=$2
+
+ fileSize1=$(ls -la $src1 | cut -f5 -d" ")
+ fileSize2=$(ls -la $src2 | cut -f5 -d" ")
+ # Add a buffer to space requirements to ensure we don't fill up boot
+ fileSize=$(($fileSize1 + $fileSize2 + 4096))
+ freeSpace=$(df --output=avail $SUBDIR/boot | sed -n '1!p')
+ # Get free space in bytes
+ freeBytes=$(expr $freeSpace \* 1000)
+
+ if [ $fileSize -lt $freeBytes ]; then
+ enoughSpace_res=1
+ else
+ enoughSpace_res=0
+ fi
+}
+
+# Compares sha1_sum's of srcfile and destfile, creates backup of destfile
+# if the two differ
+# This backup will then be available for restoration via UEFI command line
+# if the new binary fails to boot
+function preCopyBackup()
+{
+ srcfile=$1
+ destfile=$2
+
+ if [ ! -f $srcfile ]
+ then
+ echo " $srcfile not found. Nothing to do."
+ exit 1;
+ fi
+
+ if [ ! -f $destfile ]
+ then
+ return 0
+ fi
+
+ sha1_cmp $srcfile $destfile
+ if [ $sha1_cmp_res ]
+ then
+ newname="${destfile}.old"
+ cp $destfile $newname
+ fi
+
+ return 0
+}
+
+
+##################### Main #####################
+if [[ $# > 2 ]]
+then
+ printUsage
+fi
+
+# Parse cmdline
+if [[ $# > 0 ]]
+then
+ case $1 in
+ -h|--help)
+ printUsage
+ exit 0
+ ;;
+ -v|--version)
+ echo "bootloader_updater.sh version $VERSION"
+ exit 0
+ ;;
+ -p|--path)
+ if [[ $# = 1 ]]
+ then
+ echo "Invalid arguments. --path requires a second argument."
+ exit 1
+ fi
+ SUBDIR="$2"
+ ;;
+ *)
+ echo "Unrecognized argument ($1). Try again"
+ printUsage
+ exit 1
+ ;;
+ esac
+fi
+
+if [[ $EUID -ne 0 ]]; then
+ echo "Must be root to execute bootloader_updater.sh"
+ exit 1
+fi
+
+##### Mount /boot ######
+if [ -z "${SUBDIR}" ]; then
+ # Get boot partition UUID
+ BOOTUUIDENTRY=$(ls /sys/firmware/efi/efivars/LoaderDevicePartUUID*)
+ if [[ -e $BOOTUUIDENTRY ]]; then
+ UUID=$(cat $BOOTUUIDENTRY | tr -cd '[[:alnum:]]._-' | tr '[:upper:]' '[:lower:]')
+ fi
+
+ strlen=${#UUID}
+
+ IS_MOUNTED=$(mount | sed -n '/boot/p' | sed -n '/vfat/p')
+ if [[ -z "$IS_MOUNTED" ]];
+ then
+ if [ "$strlen" -gt 0 ]
+ then
+ echo "Mounting boot partition with UUID=$UUID"
+ mount /dev/disk/by-partuuid/$UUID /boot -o rw -t vfat
+ else
+ echo "Failed to identify boot partition using /sys/firmware/efi/efivars."
+ echo " Attempting to mount by-partlabel"
+ mount /dev/disk/by-partlabel/ESP /boot -o rw -t vfat
+ fi
+ mountedBoot=1
+ fi
+
+ IS_MOUNTED=$(mount | sed -n '/boot/p' | sed -n '/vfat/p')
+ if [[ -z "$IS_MOUNTED" ]];
+ then
+ echo "/boot partition was not found. Unable to continue with systemd bootloader install"
+ exit 1
+ fi
+fi
+
+
+echo "Installing new systemd bootx binary... please wait..."
+src1="$SUBDIR/usr/lib/systemd/boot/efi/systemd-bootx64.efi"
+dest1="$SUBDIR/boot/EFI/systemd/systemd-bootx64.efi"
+preCopyBackup $src1 $dest1
+src2="$SUBDIR/usr/lib/systemd/boot/efi/systemd-bootx64.efi"
+dest2="$SUBDIR/boot/EFI/Boot/BOOTX64.EFI"
+preCopyBackup $src2 $dest2
+
+
+if [ ! -d "$SUBDIR/boot/EFI/systemd/" ]; then
+ mkdir -p "$SUBDIR/boot/EFI/systemd"
+fi
+
+if [ ! -d "$SUBDIR/boot/EFI/Boot/" ]; then
+ mkdir -p "$SUBDIR/boot/EFI/Boot"
+fi
+
+function fail_msg()
+{
+ echo "$* - Aborting"
+ exit 1
+}
+
+# use clear's bootctl only.
+function installSystemd()
+{
+ if [[ -z "${SUBDIR}" ]]; then
+ /usr/bin/bootctl install || fail_msg "Installing systemd bootloader failed"
+ else
+ # HACK
+ chroot "${SUBDIR}" /usr/bin/bootctl install --no-variables --force || fail_msg "Installing systemd bootloader failed"
+ fi
+}
+
+function updateSystemd()
+{
+ if [[ -z "${SUBDIR}" ]]; then
+ /usr/bin/bootctl update || fail_msg "Updating systemd bootloader failed"
+ else
+ chroot "${SUBDIR}" /usr/bin/bootctl update --no-variables --force || fail_msg "Updating systemd bootloader failed"
+ fi
+}
+
+# Space check before copy
+enoughSpace $src1 $src2
+if [ $enoughSpace_res -eq 1 ]; then
+ if [[ ! -e "${dest1}" ]] || [[ ! -e "${dest2}" ]]; then
+ # absolutely require an install
+ installSystemd
+ fi
+ sha1_cmp "$src1" "$dest1"
+ if [ $sha1_cmp_res -eq 1 ]; then
+ updateSystemd
+ fi
+else
+ echo "Insufficient space on /boot to install new bootloader binary"
+ exit 1
+fi
+
+
+# Sync after copy to protect against VFAT corruption
+pushd "$SUBDIR/boot" > /dev/null
+sync
+popd > /dev/null
+
+# Ensure older bootloader binary is not present
+# Note this will only occur if the new systemd bootloader was installed
+rm -Rf "$SUBDIR/boot/EFI/gummiboot"
+
+# Sync after cleanup to protect against VFAT corruption
+pushd "$SUBDIR/boot" > /dev/null
+sync
+popd > /dev/null
+
+if [ -z "${SUBDIR}" ]; then
+ if [[ "${mountedBoot}" -eq 1 ]]; then
+ umount /boot
+ fi
+fi
+
+echo "systemd bootloader update sucessful."
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/tmpfiles-trigger.service b/recipes-core/swupd-client/oe-swupd-helpers/tmpfiles-trigger.service
new file mode 100644
index 0000000..71009fe
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/tmpfiles-trigger.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Create tmpfiles
+Documentation=man:systemd-tmpfiles(8)
+BindsTo=update-triggers.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/systemd-tmpfiles --create
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target b/recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target
new file mode 100644
index 0000000..d104b00
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target
@@ -0,0 +1,5 @@
+[Unit]
+Description=Post system update triggers
+Wants=ldconfig-trigger.service
+Wants=catalog-trigger.service
+Wants=tmpfiles-trigger.service
diff --git a/recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target~ b/recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target~
new file mode 100644
index 0000000..3c2e394
--- /dev/null
+++ b/recipes-core/swupd-client/oe-swupd-helpers/update-triggers.target~
@@ -0,0 +1,9 @@
+[Unit]
+Description=Post system update triggers
+Wants=ldconfig-trigger.service
+#Wants=mandb-trigger.service
+#Wants=python-trigger.service
+Wants=catalog-trigger.service
+Wants=tmpfiles-trigger.service
+#Wants=locale-archive-trigger.service
+Wants=systemd-modules-trigger.service
diff --git a/recipes-core/swupd-client/swupd-client/0001-Tolerate-quotes-in-os-release-files.patch b/recipes-core/swupd-client/swupd-client/0001-Tolerate-quotes-in-os-release-files.patch
new file mode 100644
index 0000000..49c71ae
--- /dev/null
+++ b/recipes-core/swupd-client/swupd-client/0001-Tolerate-quotes-in-os-release-files.patch
@@ -0,0 +1,59 @@
+From 586e7b927461f6604ee3a3159cd7a6d4ac22ef30 Mon Sep 17 00:00:00 2001
+From: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
+Date: Thu, 11 Feb 2016 13:29:57 +0200
+Subject: [PATCH 1/2] Tolerate quotes in os-release files
+
+Some systems like Yocto or OpenSUSE prefer to wrap values in
+/etc/os-release file with quotes always and that still conforms
+to the format defined in systemd.
+
+This patch removes quotes from the values before trying to
+transform them into integer version id.
+
+Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
+
+Upstream-Status: Backport (v3.0.0+)
+
+---
+ src/version.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/src/version.c b/src/version.c
+index 0e09cd9..83d6ad4 100644
+--- a/src/version.c
++++ b/src/version.c
+@@ -88,6 +88,7 @@ int read_version_from_subvol_file(char *path_prefix)
+ FILE *file;
+ int v = -1;
+ char *buildstamp;
++ char *src, *dest;
+
+ string_or_die(&buildstamp, "%s/usr/lib/os-release", path_prefix);
+ file = fopen(buildstamp, "rm");
+@@ -106,7 +107,22 @@ int read_version_from_subvol_file(char *path_prefix)
+ break;
+ }
+
+- if (strncmp(line,"VERSION_ID=", 11) == 0) {
++ if (strncmp(line, "VERSION_ID=", 11) == 0) {
++ src = &line[11];
++
++ /* Drop quotes and newline in value */
++ dest = src;
++ while (*src) {
++ if (*src == '\'' || *src == '"' || *src == '\n') {
++ ++src;
++ } else {
++ *dest = *src;
++ ++dest;
++ ++src;
++ }
++ }
++ *dest = 0;
++
+ v = strtoull(&line[11], NULL, 10);
+ break;
+ }
+--
+2.5.0
+
diff --git a/recipes-core/swupd-client/swupd-client/Change-systemctl-path-to-OE-systemctl-path.patch b/recipes-core/swupd-client/swupd-client/Change-systemctl-path-to-OE-systemctl-path.patch
new file mode 100644
index 0000000..5ca6373
--- /dev/null
+++ b/recipes-core/swupd-client/swupd-client/Change-systemctl-path-to-OE-systemctl-path.patch
@@ -0,0 +1,31 @@
+From 259d86e64146c3156eccfcce0351a9cdc4714766 Mon Sep 17 00:00:00 2001
+From: Jaska Uimonen <jaska.uimonen@intel.com>
+Date: Thu, 14 Jan 2016 10:17:43 +0200
+Subject: [PATCH] change systemctl path to OE systemctl path
+
+Upstream-Status: Inappropriate
+
+---
+ src/scripts.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/scripts.c b/src/scripts.c
+index e426272..9bec0f5 100644
+--- a/src/scripts.c
++++ b/src/scripts.c
+@@ -84,10 +84,10 @@ static void update_triggers(void)
+ int ret;
+ LOG_INFO(NULL, "calling systemd trigger", class_scripts, "");
+
+- ret = system("/usr/bin/systemctl daemon-reload");
++ ret = system("/bin/systemctl daemon-reload");
+ if (ret != 0)
+ LOG_ERROR(NULL, "systemd daemon reload failed", class_scripts, "%d", ret);
+- ret = system("/usr/bin/systemctl restart update-triggers.target");
++ ret = system("/bin/systemctl restart update-triggers.target");
+ if (ret != 0)
+ LOG_ERROR(NULL, "systemd update triggers failed", class_scripts, "%d", ret);
+ }
+--
+2.5.0
+
diff --git a/recipes-core/swupd-client/swupd-client/Fix-build-failure-on-Yocto.patch b/recipes-core/swupd-client/swupd-client/Fix-build-failure-on-Yocto.patch
new file mode 100644
index 0000000..73f1728
--- /dev/null
+++ b/recipes-core/swupd-client/swupd-client/Fix-build-failure-on-Yocto.patch
@@ -0,0 +1,36 @@
+From ccce73a2d703e6789ded87ca5aa9f3b7c506892a Mon Sep 17 00:00:00 2001
+From: Amarnath Valluri <amarnath.valluri@intel.com>
+Date: Thu, 7 Jan 2016 16:19:34 +0200
+Subject: [PATCH] Fix build failure on Yocto
+
+On install phase certificate files are being installed twice as included in
+_DATA twice. We can use EXTRA_DIST than dist_.
+
+Upstream-Status: Inappropriate
+
+Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
+---
+ Makefile.am | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 1e65d3d..4d15c55 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -111,11 +111,11 @@ SWUPD_CERTS = certs/157753a5.0 \
+ certs/d6325660.0 \
+ certs/d6325660.1
+ swupdcerts_DATA = $(SWUPD_CERTS)
+-dist_swupdcerts_DATA = $(SWUPD_CERTS)
+
+ EXTRA_DIST += \
+ data/check-update.service \
+- data/check-update.timer
++ data/check-update.timer \
++ $(SWUPD_CERTS)
+
+ DISTCHECK_CONFIGURE_FLAGS = \
+ --with-systemdsystemunitdir=$$dc_install_base/$(systemdunitdir)
+--
+2.1.4
+
diff --git a/recipes-core/swupd-client/swupd-client/Right-usage-of-AC_ARG_ENABLE-on-bzip2.patch b/recipes-core/swupd-client/swupd-client/Right-usage-of-AC_ARG_ENABLE-on-bzip2.patch
new file mode 100644
index 0000000..e5b53ef
--- /dev/null
+++ b/recipes-core/swupd-client/swupd-client/Right-usage-of-AC_ARG_ENABLE-on-bzip2.patch
@@ -0,0 +1,38 @@
+From d80ae9954c5e5b720766274249dbf5309b7c70a9 Mon Sep 17 00:00:00 2001
+From: Amarnath Valluri <amarnath.valluri@intel.com>
+Date: Wed, 13 Jan 2016 15:46:19 +0200
+Subject: [PATCH] Right usage of AC_ARG_ENABLE on bzip2
+
+Upstream-Status: Pending[Not submitted]
+
+Signed-off-by: Amarnath Valluri <amarnath.valluri@intel.com>
+---
+ configure.ac | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index f94a17d..b11ef0a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -20,11 +20,13 @@ AC_CONFIG_HEADERS([config.h])
+ PKG_CHECK_MODULES([lzma], [liblzma])
+ PKG_CHECK_MODULES([zlib], [zlib])
+ AC_ARG_ENABLE(
+- bzip2,
+- AS_HELP_STRING([--disable-bzip2],[Do not use bzip2 compression (uses bzip2 by default)]),
+- AC_DEFINE(SWUPD_WITHOUT_BZIP2,1,[Do not use bzip2 compression]) ,
+- AC_DEFINE(SWUPD_WITH_BZIP2,1,[Use bzip2 compression])
+- AC_CHECK_LIB([bz2], [BZ2_bzBuffToBuffCompress], [], [AC_MSG_ERROR([the libbz2 library is missing])])
++ [bzip2],
++ AS_HELP_STRING([--disable-bzip2],[Do not use bzip2 compression (uses bzip2 by default)])
++)
++AS_IF([test "x$enable_bzip2" = "xyes" ],
++ [AC_DEFINE(SWUPD_WITH_BZIP2,1,[Use bzip2 compression])
++ AC_CHECK_LIB([bz2], [BZ2_bzBuffToBuffCompress], [], [AC_MSG_ERROR([the libbz2 library is missing])])],
++ [AC_DEFINE(SWUPD_WITHOUT_BZIP2,1,[Do not use bzip2 compression])]
+ )
+
+ AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
+--
+2.1.4
+
diff --git a/recipes-core/swupd-client/swupd-client_2.87.bb b/recipes-core/swupd-client/swupd-client_2.87.bb
new file mode 100644
index 0000000..8226624
--- /dev/null
+++ b/recipes-core/swupd-client/swupd-client_2.87.bb
@@ -0,0 +1,49 @@
+SUMMARY = "swupd sofware update from Clear Linux - client component"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://COPYING;md5=04d0b48662817042d80393e7511fa41b \
+ file://bsdiff/LICENSE;md5=0dbe7a50f028269750631fcbded3846a"
+
+SRC_URI = "\
+ https://download.clearlinux.org/releases/5700/clear/source/SRPMS/${BPN}-${PV}-105.src.rpm;extract=${BP}.tar.gz \
+ file://Fix-build-failure-on-Yocto.patch \
+ file://Right-usage-of-AC_ARG_ENABLE-on-bzip2.patch \
+ file://Change-systemctl-path-to-OE-systemctl-path.patch \
+ file://0001-Tolerate-quotes-in-os-release-files.patch \
+"
+
+SRC_URI[md5sum] = "5d272c62edb8a9c576005ac5e1182ea3"
+SRC_URI[sha256sum] = "45df259a7dc2fed985ee9961e112120fc46670dd75476c3262fc6804b1c66fb8"
+
+DEPENDS = "glib-2.0 curl zlib bzip2 xz openssl"
+RDEPENDS_${PN} = "gzip bzip2 tar xz"
+RDEPENDS_${PN}_class-target = "oe-swupd-helpers"
+# We check /etc/os-release for the current OS version number
+RRECOMMENDS_${PN}_class-target = "os-release"
+
+inherit pkgconfig autotools-brokensep systemd
+
+EXTRA_OECONF = "--with-systemdsystemunitdir=${systemd_system_unitdir}"
+
+#TODO: create and install /var/lib/swupd/{delta,staged/download}
+do_install_append () {
+ # swupd-client 2.87 doesn't (succesfully) create these and fails to update
+ # should they not exist. This is due to a bash-specific shell command
+ # called to create the directories 'mkdir -p /var/lib/{delta,staged,download}'
+ install -d ${D}/var/lib/swupd/delta
+ install -d ${D}/var/lib/swupd/download
+ install -d ${D}/var/lib/swupd/staged
+
+ # TODO: This should be a less os-specific directory and not hard-code datadir
+ install -d ${D}/usr/share/clear/bundles
+}
+
+FILES_${PN} += "\
+ /usr/share/clear \
+ ${systemd_system_unitdir}/multi-user.target.wants* \
+ /var/lib/swupd \
+"
+
+SYSTEMD_SERVICE_${PN} = "check-update.timer check-update.service"
+SYSTEMD_AUTO_ENABLE_${PN} = "disable"
+
+BBCLASSEXTEND = "native"
diff --git a/recipes-core/swupd-server/swupd-server/0001-Add-option-S-to-take-the-state-data-dir-as-an-argume.patch b/recipes-core/swupd-server/swupd-server/0001-Add-option-S-to-take-the-state-data-dir-as-an-argume.patch
new file mode 100644
index 0000000..b771e0a
--- /dev/null
+++ b/recipes-core/swupd-server/swupd-server/0001-Add-option-S-to-take-the-state-data-dir-as-an-argume.patch
@@ -0,0 +1,782 @@
+From 83fbb8a78594cf9047e37ed0aa1e931b9d691f2a Mon Sep 17 00:00:00 2001
+From: Joshua Lock <joshua.g.lock@intel.com>
+Date: Thu, 28 Jan 2016 10:24:56 +0000
+Subject: [PATCH 1/2] Add option -S to take the state data dir as an argument
+
+The optional -S option expects a full path to a directory
+swupd-server should use instead of the default /var/lib/update.
+
+Signed-off-by: Joshua Lock <joshua.g.lock@intel.com>
+
+Upstream-Status: Accepted (v3.0+)
+
+---
+ include/swupd.h | 11 ++++++---
+ src/analyze_fs.c | 4 ++--
+ src/chroot.c | 13 ++++++-----
+ src/globals.c | 53 +++++++++++++++++++++++++++++++++++++++++
+ src/main.c | 50 ++++++++++++++++++++++++++++++---------
+ src/make_fullfiles.c | 63 +++++++++++++++++++++++++++++++++++++++++++++----
+ src/make_packs.c | 66 ++++++++++++++++++++++++++++++++++++++++++++--------
+ src/pack.c | 65 +++++++++++++++++++++++++++------------------------
+ src/rename.c | 6 ++---
+ src/versions.c | 6 ++---
+ 10 files changed, 264 insertions(+), 73 deletions(-)
+
+diff --git a/include/swupd.h b/include/swupd.h
+index 775c28e..58307d9 100644
+--- a/include/swupd.h
++++ b/include/swupd.h
+@@ -16,9 +16,6 @@
+ #define SWUPD_DEFAULT_FORMAT 3
+
+ #define SWUPD_SERVER_STATE_DIR "/var/lib/update"
+-#define PACKSTAGE_DIR SWUPD_SERVER_STATE_DIR "/packstage"
+-#define IMAGE_DIR SWUPD_SERVER_STATE_DIR "/image"
+-#define STAGING_DIR SWUPD_SERVER_STATE_DIR "/www"
+
+ #if SWUPD_WITH_SELINUX
+ #define TAR_PERM_ATTR_ARGS "--preserve-permissions --xattrs --xattrs-include='*' --selinux"
+@@ -123,9 +120,17 @@ extern int newversion;
+ extern int minversion;
+ extern char *format_string;
+
++extern char *state_dir;
++extern char *packstage_dir;
++extern char *image_dir;
++extern char *staging_dir;
++
+ extern bool init_globals(void);
+ extern void free_globals(void);
+ extern bool set_format_string(char *);
++extern bool set_state_dir(char *);
++extern bool init_state_globals(void);
++extern void free_state_globals(void);
+
+ extern int file_sort_hash(gconstpointer a, gconstpointer b);
+ extern int file_sort_filename(gconstpointer a, gconstpointer b);
+diff --git a/src/analyze_fs.c b/src/analyze_fs.c
+index 2326237..fdc359a 100644
+--- a/src/analyze_fs.c
++++ b/src/analyze_fs.c
+@@ -389,7 +389,7 @@ struct manifest *full_manifest_from_directory(int version)
+
+ manifest = alloc_manifest(version, "full");
+
+- string_or_die(&dir, "%s/%i/full", IMAGE_DIR, version);
++ string_or_die(&dir, "%s/%i/full", image_dir, version);
+
+ threadpool = g_thread_pool_new(get_hash, dir, 12, FALSE, NULL);
+
+@@ -413,7 +413,7 @@ struct manifest *sub_manifest_from_directory(char *component, int version)
+
+ manifest = alloc_manifest(version, component);
+
+- string_or_die(&dir, "%s/%i/%s", IMAGE_DIR, version, component);
++ string_or_die(&dir, "%s/%i/%s", image_dir, version, component);
+
+ iterate_directory(manifest, dir, "", false);
+
+diff --git a/src/chroot.c b/src/chroot.c
+index 1d7df95..c5bb942 100644
+--- a/src/chroot.c
++++ b/src/chroot.c
+@@ -40,14 +40,15 @@ void chroot_create_full(int newversion)
+ char * command;
+ char *full_dir;
+
+- string_or_die(&full_dir, "%s/%i/full/", IMAGE_DIR, newversion);
++ string_or_die(&full_dir, "%s/%i/full/ ", image_dir, newversion);
+
+ g_mkdir_with_parents(full_dir, S_IRWXU);
+
+
+ /* start with base */
+ LOG(NULL, "Copying chroot os-core to full", "");
+- string_or_die(&command, "rsync -aAX " IMAGE_DIR "/%i/os-core/ %s", newversion, full_dir);
++ string_or_die(&command, "rsync -aAX %s/%i/os-core/ %s",
++ image_dir, newversion, full_dir);
+ ret = system(command);
+ assert(ret==0);
+ free(command);
+@@ -60,8 +61,8 @@ void chroot_create_full(int newversion)
+ }
+
+ LOG(NULL, "Overlaying bundle chroot onto full", "%s", group);
+- string_or_die(&command, "rsync -aAX --ignore-existing " IMAGE_DIR "/%i/%s/ %s",
+- newversion, group, full_dir);
++ string_or_die(&command, "rsync -aAX --ignore-existing %s/%i/%s/ %s",
++ image_dir, newversion, group, full_dir);
+ ret = system(command);
+ assert(ret==0);
+ free(command);
+@@ -80,8 +81,8 @@ void chroot_create_full(int newversion)
+ }
+
+ LOG(NULL, "Recopy bundle chroot out of full", "%s", group);
+- string_or_die(&command, "rsync -aAX --existing %s " IMAGE_DIR "/%i/%s",
+- full_dir, newversion, group);
++ string_or_die(&command, "rsync -aAX --existing %s %s/%i/%s",
++ full_dir, image_dir, newversion, group);
+ ret = system(command);
+ assert(ret==0);
+ free(command);
+diff --git a/src/globals.c b/src/globals.c
+index 32dc793..ff6a3fa 100644
+--- a/src/globals.c
++++ b/src/globals.c
+@@ -34,6 +34,11 @@ int newversion = -1;
+ int minversion = 0;
+ char *format_string = NULL;
+
++char *state_dir = NULL;
++char *packstage_dir = NULL;
++char *image_dir = NULL;
++char *staging_dir = NULL;
++
+ bool set_format_string(char *userinput)
+ {
+ int version;
+@@ -52,12 +57,36 @@ bool set_format_string(char *userinput)
+ return true;
+ }
+
++bool set_state_dir(char *dir)
++{
++ if (dir == NULL || dir[0] == '\0') {
++ return false;
++ }
++
++ /* TODO: more validation of input? */
++ if (dir[0] != '/') {
++ printf("statedir must be a full path starting with '/', not '%c'\n", dir[0]);
++ return false;
++ }
++
++ if (state_dir) {
++ free(state_dir);
++ }
++ string_or_die(&state_dir, "%s", dir);
++
++ return true;
++}
++
+ bool init_globals(void)
+ {
+ if (format_string == NULL) {
+ string_or_die(&format_string, "%d", SWUPD_DEFAULT_FORMAT);
+ }
+
++ if (!init_state_globals()) {
++ return false;
++ }
++
+ if (newversion == -1) {
+ printf("Missing version parameter: No new version number specified\n");
+ return false;
+@@ -71,4 +100,28 @@ bool init_globals(void)
+ void free_globals(void)
+ {
+ free(format_string);
++ free(state_dir);
++ free(packstage_dir);
++ free(image_dir);
++ free(staging_dir);
++}
++
++bool init_state_globals(void)
++{
++ if (state_dir == NULL) {
++ string_or_die(&state_dir, "%s", SWUPD_SERVER_STATE_DIR);
++ }
++ string_or_die(&packstage_dir, "%s/%s", state_dir, "packstage");
++ string_or_die(&image_dir, "%s/%s", state_dir, "image");
++ string_or_die(&staging_dir, "%s/%s", state_dir, "www");
++
++ return true;
++}
++
++void free_state_globals(void)
++{
++ free(state_dir);
++ free(packstage_dir);
++ free(image_dir);
++ free(staging_dir);
+ }
+diff --git a/src/main.c b/src/main.c
+index 7ed71d5..3d5f24a 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -55,6 +55,7 @@ static const struct option prog_opts[] = {
+ {"minversion", required_argument, 0, 'm'},
+ {"format", required_argument, 0, 'F'},
+ {"getformat", no_argument, 0, 'g'},
++ {"statedir", required_argument, 0, 'S'},
+ {0, 0, 0, 0}
+ };
+
+@@ -70,6 +71,7 @@ static void print_help(const char *name) {
+ printf(" -m, --minversion Optional minimum file version to write into manifests per file\n");
+ printf(" -F, --format Optional format string [ default:=%d ]\n", SWUPD_DEFAULT_FORMAT);
+ printf(" -g, --getformat Print current format string and exit\n");
++ printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
+ printf("\n");
+ }
+
+@@ -77,7 +79,7 @@ static bool parse_options(int argc, char **argv)
+ {
+ int opt;
+
+- while ((opt = getopt_long(argc, argv, "hvo:m:F:g", prog_opts, NULL)) != -1) {
++ while ((opt = getopt_long(argc, argv, "hvo:m:F:g:S:", prog_opts, NULL)) != -1) {
+ switch (opt) {
+ case '?':
+ case 'h':
+@@ -107,6 +109,12 @@ static bool parse_options(int argc, char **argv)
+ return false;
+ }
+ break;
++ case 'S':
++ if (!optarg || !set_state_dir(optarg)) {
++ printf("Invalid --statedir argument ''%s'\n\n", optarg);
++ return false;
++ }
++ break;
+ case 'g':
+ if (format_string == NULL) {
+ printf("%d\n", SWUPD_DEFAULT_FORMAT);
+@@ -138,9 +146,14 @@ static void check_root(void)
+ static void populate_dirs(int version)
+ {
+ char *newversiondir;
+- string_or_die(&newversiondir, "%s/%d", IMAGE_DIR, version);
++
++ string_or_die(&newversiondir, "%s/%d", image_dir, version);
+
+ if ((access(newversiondir, F_OK | R_OK) != 0) && (version == 0)) {
++ char *latestpath = NULL;
++
++ string_or_die(&latestpath, "%s/latest.version", image_dir);
++
+ printf("** %s does not exist... creating and populating\n", newversiondir);
+ if (mkdir(newversiondir, 0755) != 0) {
+ printf("Failed to create directory\n");
+@@ -151,14 +164,17 @@ static void populate_dirs(int version)
+ }
+
+ FILE *latestver;
+- latestver = fopen_exclusive(IMAGE_DIR "/latest.version");
++ latestver = fopen_exclusive(latestpath);
+ if (latestver == NULL) {
+- printf("Failed to create %s/latest.version\n", IMAGE_DIR);
++ printf("Failed to create %s\n", latestpath);
++ free(latestpath);
+ return;
+ }
+ if (fwrite("0\n", 2, 1, latestver) != 1) {
+ LOG(NULL, "Failed to write latestver", "errno: %d", errno);
+ }
++
++ free(latestpath);
+ fclose(latestver);
+ }
+ /* groups don't exist in version 0 */
+@@ -170,7 +186,7 @@ static void populate_dirs(int version)
+ break;
+ }
+
+- string_or_die(&newversiondir, "%s/%d/%s", IMAGE_DIR, version, group);
++ string_or_die(&newversiondir, "%s/%d/%s", image_dir, version, group);
+
+ /* Create the bundle directory(s) as needed */
+ if (access(newversiondir, F_OK | R_OK) != 0) {
+@@ -186,13 +202,18 @@ static void populate_dirs(int version)
+
+ static int check_build_env(void)
+ {
+- if (access(SWUPD_SERVER_STATE_DIR "/temp", F_OK | R_OK) != 0) {
+- LOG(NULL, "/var/lib/update/temp does not exist...creating directory", "");
+- if (mkdir(SWUPD_SERVER_STATE_DIR "/temp", 0755) != 0) {
++ char *temp_dir = NULL;
++ string_or_die(&temp_dir, "%s/%s", state_dir, "/temp");
++
++ if (access(temp_dir, F_OK | R_OK) != 0) {
++ LOG(NULL, "%s", "does not exist...creating directory", temp_dir);
++ if (mkdir(temp_dir, 0755) != 0) {
+ printf("Failed to create build directory, EXITING\n");
++ free(temp_dir);
+ return -errno;
+ }
+ }
++ free(temp_dir);
+
+ return 0;
+ }
+@@ -217,6 +238,8 @@ int main(int argc, char **argv)
+ int exit_status = EXIT_FAILURE;
+ int ret;
+
++ char *file_path = NULL;
++
+ /* keep valgrind working well */
+ setenv("G_SLICE", "always-malloc", 0);
+
+@@ -240,12 +263,17 @@ int main(int argc, char **argv)
+ }
+
+
+- if (!read_configuration_file(SWUPD_SERVER_STATE_DIR "/server.ini")) {
+- printf("Failed to read " SWUPD_SERVER_STATE_DIR "/server.ini configuration file!\n");
++ string_or_die(&file_path, "%s/server.ini", state_dir);
++ if (!read_configuration_file(file_path)) {
++ printf("Failed to read %s configuration file!\n", state_dir);
++ free(file_path);
+ goto exit;
+ }
++ free(file_path);
+
+- read_group_file(SWUPD_SERVER_STATE_DIR "/groups.ini");
++ string_or_die(&file_path, "%s/groups.ini", state_dir);
++ read_group_file(file_path);
++ free(file_path);
+
+ read_current_version("latest.version");
+ printf("Last processed version is %i\n", current_version);
+diff --git a/src/make_fullfiles.c b/src/make_fullfiles.c
+index e755a33..bf97a1c 100644
+--- a/src/make_fullfiles.c
++++ b/src/make_fullfiles.c
+@@ -27,8 +27,52 @@
+ #include <string.h>
+ #include <assert.h>
+
++#include <getopt.h>
++
+ #include <swupd.h>
+
++static const struct option prog_opts[] = {
++ {"help", no_argument, 0, 'h'},
++ {"statedir", required_argument, 0, 'S'},
++ {0, 0, 0, 0}
++};
++
++static void usage(const char *name)
++{
++ printf("usage:\n");
++ printf(" %s <version>\n\n", name);
++ printf("Help options:\n");
++ printf(" -h, --help Show help options\n");
++ printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
++ printf("\n");
++}
++
++static bool parse_options(int argc, char **argv)
++{
++ int opt;
++
++ while ((opt = getopt_long(argc, argv, "hS:", prog_opts, NULL)) != -1) {
++ switch (opt) {
++ case '?':
++ case 'h':
++ usage(argv[0]);
++ return false;
++ case 'S':
++ if (!optarg || !set_state_dir(optarg)) {
++ printf("Invalid --statedir argument '%s'\n\n", optarg);
++ return false;
++ }
++ break;
++ }
++ }
++
++ if (!init_state_globals()) {
++ return false;
++ }
++
++ return true;
++}
++
+ static void banner(void)
+ {
+ printf(PACKAGE_NAME " update creator -- fullfiles -- version " PACKAGE_VERSION "\n");
+@@ -51,20 +95,29 @@ int main(int argc, char **argv)
+ {
+ struct manifest *manifest;
+ int version;
++ char *file_path = NULL;
+
+ /* keep valgrind working well */
+ setenv("G_SLICE", "always-malloc", 0);
+
++ if (!parse_options(argc, argv)) {
++ free_state_globals();
++ return EXIT_FAILURE;
++ }
+ banner();
+ check_root();
+
+- read_configuration_file(SWUPD_SERVER_STATE_DIR "/server.ini");
++ string_or_die(&file_path, "%s/server.ini", state_dir);
++ read_configuration_file(file_path);
++ free(file_path);
+
+- if (argc < 1) {
+- printf("Usage:\n\tswupd_make_fullfiles <version>\n\n");
++ if (argc - optind < 1) {
++ usage(argv[0]);
++ free_state_globals();
+ exit(EXIT_FAILURE);
+ }
+- version = strtoull(argv[1], NULL, 10);
++
++ version = strtoull(argv[optind++], NULL, 10);
+ if (version < 0) {
+ printf("Usage:\n\tswupd_make_fullfiles <version>\n\n");
+ exit(EXIT_FAILURE);
+@@ -73,5 +126,7 @@ int main(int argc, char **argv)
+ manifest = manifest_from_file(version, "full");
+ create_fullfiles(manifest);
+
++ free_state_globals();
++
+ return EXIT_SUCCESS;
+ }
+diff --git a/src/make_packs.c b/src/make_packs.c
+index 20fbd67..2b62b8a 100644
+--- a/src/make_packs.c
++++ b/src/make_packs.c
+@@ -33,6 +33,8 @@
+
+ #include <glib.h>
+
++#include <getopt.h>
++
+ #include <swupd.h>
+
+ static void banner(void)
+@@ -43,12 +45,47 @@ static void banner(void)
+ printf("\n");
+ }
+
+-static void usage(void)
++static const struct option prog_opts[] = {
++ {"help", no_argument, 0, 'h'},
++ {"statedir", required_argument, 0, 'S'},
++ {0, 0, 0, 0}
++};
++
++static void usage(const char *name)
+ {
+ printf("usage:\n");
+- printf(" swupd_make_pack <start version> <latest version> <bundle>\n");
++ printf(" %s <start version> <latest version> <bundle>\n\n", name);
++ printf("Help options:\n");
++ printf(" -h, --help Show help options\n");
++ printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
+ printf("\n");
+- exit(EXIT_FAILURE);
++}
++
++static bool parse_options(int argc, char **argv)
++{
++ int opt;
++
++ while ((opt = getopt_long(argc, argv, "hS:", prog_opts, NULL)) != -1) {
++ switch (opt) {
++ case '?':
++ case 'h':
++ usage(argv[0]);
++ return false;
++ case 'S':
++ if (!optarg || !set_state_dir(optarg)) {
++ printf("Invalid --statedir argument ''%s'\n\n", optarg);
++ return false;
++ }
++ break;
++ }
++ }
++
++ /* FIXME: *_state_globals() are ugly hacks */
++ if (!init_state_globals()) {
++ return false;
++ }
++
++ return true;
+ }
+
+ int main(int argc, char **argv)
+@@ -59,11 +96,17 @@ int main(int argc, char **argv)
+ char *module;
+ struct packdata *pack;
+ int exit_status = EXIT_FAILURE;
++ char *file_path = NULL;
+
++ if (!parse_options(argc, argv)) {
++ free_state_globals();
++ return EXIT_FAILURE;
++ }
+ banner();
+
+- if (argc != 4) {
+- usage();
++ if (argc - optind != 3) {
++ usage(argv[0]);
++ exit(EXIT_FAILURE);
+ }
+
+ /* FIXME: should use "end_version" not "0" and a unique filename
+@@ -76,12 +119,13 @@ int main(int argc, char **argv)
+ return exit_status;
+ }
+
+- read_configuration_file(SWUPD_SERVER_STATE_DIR "/server.ini");
+-
++ string_or_die(&file_path, "%s/server.ini", state_dir);
++ read_configuration_file(file_path);
++ free(file_path);
+
+- start_version = strtoull(argv[1], NULL, 10);
+- end_version = strtoull(argv[2], NULL, 10);
+- module = argv[3];
++ start_version = strtoull(argv[optind++], NULL, 10);
++ end_version = strtoull(argv[optind++], NULL, 10);
++ module = argv[optind++];
+
+ if ((start_version < 0) ||
+ (end_version == 0) ||
+@@ -108,5 +152,7 @@ int main(int argc, char **argv)
+ printf("Pack creation %s (pack-%s %i to %li)\n",
+ exit_status == EXIT_SUCCESS ? "complete" : "failed",
+ module, start_version, end_version);
++
++ free_state_globals();
+ return exit_status;
+ }
+diff --git a/src/pack.c b/src/pack.c
+index e96e9a4..036853f 100644
+--- a/src/pack.c
++++ b/src/pack.c
+@@ -46,21 +46,21 @@ static void empty_pack_stage(int full, int version, char *module)
+ int ret;
+
+ // clean any stale data (eg: re-run after a failure)
+- string_or_die(&cmd, "rm -rf " PACKSTAGE_DIR "/%s/%i/", module, version);
++ string_or_die(&cmd, "rm -rf %s/%s/%i/", packstage_dir, module, version);
+ ret = system(cmd);
+ if (ret) {
+- fprintf(stderr, "Failed to clean " PACKSTAGE_DIR "/%s/%i\n",
+- module, version);
++ fprintf(stderr, "Failed to clean %s/%s/%i\n",
++ packstage_dir, module, version);
+ exit(EXIT_FAILURE);
+ }
+ free(cmd);
+
+ if (!full) {
+ // (re)create module/version/{delta,staged}
+- string_or_die(&path, PACKSTAGE_DIR "/%s/%i/delta", module, version);
++ string_or_die(&path, "%s/%s/%i/delta", packstage_dir, module, version);
+ g_mkdir_with_parents(path, S_IRWXU | S_IRWXG);
+ free(path);
+- string_or_die(&path, PACKSTAGE_DIR "/%s/%i/staged", module, version);
++ string_or_die(&path, "%s/%s/%i/staged", packstage_dir, module, version);
+ g_mkdir_with_parents(path, S_IRWXU | S_IRWXG);
+ free(path);
+ }
+@@ -76,7 +76,7 @@ static void explode_pack_stage(int version, char *module)
+ struct stat buf;
+ char *path;
+
+- string_or_die(&path, PACKSTAGE_DIR "/%s/%i/staged", module, version);
++ string_or_die(&path, "%s/%s/%i/staged", packstage_dir, module, version);
+ g_mkdir_with_parents(path, S_IRWXU | S_IRWXG);
+ dir = opendir(path);
+ if (!dir) {
+@@ -99,7 +99,8 @@ static void explode_pack_stage(int version, char *module)
+ continue;
+ }
+
+- string_or_die(&path, PACKSTAGE_DIR "/%s/%i/staged/%s", module, version, entry->d_name);
++ string_or_die(&path, "%s/%s/%i/staged/%s",
++ packstage_dir, module, version, entry->d_name);
+ ret = stat(path, &buf);
+ if (ret) {
+ free(path);
+@@ -112,8 +113,8 @@ static void explode_pack_stage(int version, char *module)
+ * the resulting pack is slightly smaller, and in addition, we're saving CPU
+ * time on the client...
+ */
+- string_or_die(&tar, "tar --directory=" PACKSTAGE_DIR "/%s/%i/staged --warning=no-timestamp "
+- TAR_PERM_ATTR_ARGS " -axf %s", module, version, path);
++ string_or_die(&tar, "tar --directory=%s/%s/%i/staged --warning=no-timestamp "
++ TAR_PERM_ATTR_ARGS " -axf %s", packstage_dir, module, version, path);
+ ret = system(tar);
+ if (!ret) {
+ unlink(path);
+@@ -162,8 +163,8 @@ static void make_pack_full_files(struct packdata *pack)
+ if ((!file->peer || file->peer->is_deleted) && !file->is_deleted && !file->rename_peer) {
+ char *from, *to;
+ /* hardlink each file that is in <end> but not in <X> */
+- string_or_die(&from, STAGING_DIR "/%i/files/%s.tar", file->last_change, file->hash);
+- string_or_die(&to, PACKSTAGE_DIR "/%s/%i/staged/%s.tar", pack->module, pack->from, file->hash);
++ string_or_die(&from, "%s/%i/files/%s.tar", staging_dir, file->last_change, file->hash);
++ string_or_die(&to, "%s/%s/%i/staged/%s.tar", packstage_dir, pack->module, pack->from, file->hash);
+ ret = link(from, to);
+ if (ret) {
+ if (errno != EEXIST) {
+@@ -228,8 +229,8 @@ static GList *consolidate_packs_delta_files(GList *files, struct packdata *pack)
+ continue;
+ }
+
+- string_or_die(&from, STAGING_DIR "/%i/delta/%i-%i-%s", file->last_change,
+- file->peer->last_change, file->last_change, file->hash);
++ string_or_die(&from, "%s/%i/delta/%i-%i-%s", staging_dir, file->last_change,
++ file->peer->last_change, file->last_change, file->hash);
+
+ ret = stat(from, &stat_delta);
+ if (ret && !find_file_in_list(files, file)) {
+@@ -284,8 +285,8 @@ static int write_pack_signature(struct packdata *pack)
+ char *filename = NULL;
+ int ret = -1;
+
+- string_or_die(&filename, STAGING_DIR "/%i/pack-%s-from-%i.tar",
+- pack->to, pack->module, pack->from);
++ string_or_die(&filename, "%s/%i/pack-%s-from-%i.tar",
++ staging_dir, pack->to, pack->module, pack->from);
+ if (!signature_sign(filename)) {
+ fprintf(stderr, "Creating signature for '%s' failed\n", filename);
+ goto exit;
+@@ -324,12 +325,14 @@ static int make_final_pack(struct packdata *pack)
+
+ /* for each file changed since <X> */
+ /* locate delta, check if the diff it's from is >= <X> */
+- string_or_die(&from, STAGING_DIR "/%i/delta/%i-%i-%s", file->last_change,
+- file->peer->last_change, file->last_change, file->hash);
+- string_or_die(&to, PACKSTAGE_DIR "/%s/%i/delta/%i-%i-%s", pack->module, pack->from,
++ string_or_die(&from, "%s/%i/delta/%i-%i-%s", staging_dir, file->last_change,
+ file->peer->last_change, file->last_change, file->hash);
+- string_or_die(&tarfrom, STAGING_DIR "/%i/files/%s.tar", file->last_change, file->hash);
+- string_or_die(&tarto, PACKSTAGE_DIR "/%s/%i/staged/%s.tar", pack->module, pack->from, file->hash);
++ string_or_die(&to, "%s/%s/%i/delta/%i-%i-%s", packstage_dir, pack->module,
++ pack->from, file->peer->last_change, file->last_change, file->hash);
++ string_or_die(&tarfrom, "%s/%i/files/%s.tar", staging_dir,
++ file->last_change, file->hash);
++ string_or_die(&tarto, "%s/%s/%i/staged/%s.tar", packstage_dir, pack->module,
++ pack->from, file->hash);
+
+ ret = stat(from, &stat_delta);
+ if (ret) {
+@@ -388,8 +391,8 @@ static int make_final_pack(struct packdata *pack)
+ char *from, *to;
+ struct stat st;
+
+- string_or_die(&from, STAGING_DIR "/%i/Manifest-%s-delta-from-%i",
+- pack->to, pack->module, pack->from);
++ string_or_die(&from, "%s/%i/Manifest-%s-delta-from-%i",
++ staging_dir, pack->to, pack->module, pack->from);
+
+ ret = stat(from, &st);
+ if (ret) {
+@@ -398,8 +401,8 @@ static int make_final_pack(struct packdata *pack)
+ }
+
+
+- string_or_die(&to, PACKSTAGE_DIR "/%s/%i/Manifest-%s-delta-from-%i",
+- pack->module, pack->from, pack->module, pack->from);
++ string_or_die(&to, "%s/%s/%i/Manifest-%s-delta-from-%i",
++ packstage_dir, pack->module, pack->from, pack->module, pack->from);
+
+ ret = link(from, to);
+ if (ret) {
+@@ -416,16 +419,16 @@ static int make_final_pack(struct packdata *pack)
+ char *from, *to;
+ struct stat st;
+
+- string_or_die(&from, STAGING_DIR "/%i/Manifest-%s-delta-from-%i",
+- pack->to, "MoM", pack->from);
++ string_or_die(&from, "%s/%i/Manifest-%s-delta-from-%i",
++ staging_dir, pack->to, "MoM", pack->from);
+ ret = stat(from, &st);
+ if (ret) {
+ LOG(NULL, "Making extra manifest delta", "MoM: %i->%i", pack->from, pack->to);
+ create_manifest_delta(pack->from, pack->to, "MoM");
+ }
+
+- string_or_die(&to, PACKSTAGE_DIR "/%s/%i/Manifest-%s-delta-from-%i",
+- pack->module, pack->from, "MoM", pack->from);
++ string_or_die(&to, "%s/%s/%i/Manifest-%s-delta-from-%i",
++ packstage_dir, pack->module, pack->from, "MoM", pack->from);
+
+ ret = link(from, to);
+ if (ret) {
+@@ -439,9 +442,9 @@ static int make_final_pack(struct packdata *pack)
+
+ /* tar the staging directory up */
+ LOG(NULL, "starting tar for pack", "%s: %i to %i", pack->module, pack->from, pack->to);
+- string_or_die(&tar, "tar " TAR_PERM_ATTR_ARGS " --directory=" PACKSTAGE_DIR "/%s/%i/ "
+- "--numeric-owner -Jcf " STAGING_DIR "/%i/pack-%s-from-%i.tar delta staged",
+- pack->module, pack->from, pack->to, pack->module, pack->from);
++ string_or_die(&tar, "tar " TAR_PERM_ATTR_ARGS " --directory=%s/%s/%i/ "
++ "--numeric-owner -Jcf %s/%i/pack-%s-from-%i.tar delta staged",
++ packstage_dir, pack->module, pack->from, staging_dir, pack->to, pack->module, pack->from);
+ ret = system(tar);
+ free(tar);
+ LOG(NULL, "finished tar for pack", "%s: %i to %i", pack->module, pack->from, pack->to);
+diff --git a/src/rename.c b/src/rename.c
+index 70f9006..5ea979c 100644
+--- a/src/rename.c
++++ b/src/rename.c
+@@ -153,7 +153,7 @@ static void precompute_file_data(struct manifest *manifest, struct file *file, i
+ }
+
+ if (manifest) {
+- string_or_die(&filename, "%s/%i/%s/%s", IMAGE_DIR, manifest->version, manifest->component, file->filename);
++ string_or_die(&filename, "%s/%i/%s/%s", image_dir, manifest->version, manifest->component, file->filename);
+ } else if (old_rename) {
+ item = g_list_first(last_versions_list);
+ while (item) {
+@@ -161,13 +161,13 @@ static void precompute_file_data(struct manifest *manifest, struct file *file, i
+ item = g_list_next(item);
+
+ free(filename);
+- string_or_die(&filename, "%s/%i/full/%s", IMAGE_DIR, last_change, file->filename);
++ string_or_die(&filename, "%s/%i/full/%s", image_dir, last_change, file->filename);
+ if (!lstat(filename, &buf)) {
+ break;
+ }
+ }
+ } else {
+- string_or_die(&filename, "%s/%i/full/%s", IMAGE_DIR, file->last_change, file->filename);
++ string_or_die(&filename, "%s/%i/full/%s", image_dir, file->last_change, file->filename);
+ }
+
+ /* make sure file->stat.st_size is valid */
+diff --git a/src/versions.c b/src/versions.c
+index 6860160..0fb729a 100644
+--- a/src/versions.c
++++ b/src/versions.c
+@@ -215,10 +215,10 @@ GList *get_last_versions_list(int next_version, int max_versions)
+ int idx, build_num, build_type, jump_point;
+ int jump_point_found;
+
+- dir = opendir(STAGING_DIR);
++ dir = opendir(staging_dir);
+ if (dir == NULL) {
+ LOG(NULL, "Cannot open directory", "dir_path= %s, strerror= %s",
+- STAGING_DIR, strerror(errno));
++ staging_dir, strerror(errno));
+ return NULL;
+ }
+
+@@ -228,7 +228,7 @@ GList *get_last_versions_list(int next_version, int max_versions)
+ }
+
+ free(filename);
+- string_or_die(&filename, STAGING_DIR "/%s", entry.d_name);
++ string_or_die(&filename, "%s/%s", staging_dir, entry.d_name);
+
+ if (lstat(filename, &stat)) {
+ LOG(NULL, "lstat failed", "path= %s, strerror= %s",
+--
+2.5.0
+
diff --git a/recipes-core/swupd-server/swupd-server/0001-Add-system_argv-helper-for-safer-calls-to-system-uti.patch b/recipes-core/swupd-server/swupd-server/0001-Add-system_argv-helper-for-safer-calls-to-system-uti.patch
new file mode 100644
index 0000000..f94305e
--- /dev/null
+++ b/recipes-core/swupd-server/swupd-server/0001-Add-system_argv-helper-for-safer-calls-to-system-uti.patch
@@ -0,0 +1,133 @@
+From 8f24ef7f857c9d2c2eb2601a2a1e06123c086001 Mon Sep 17 00:00:00 2001
+From: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
+Date: Fri, 29 Jan 2016 17:48:46 +0200
+Subject: [PATCH swupd-server 1/2] Add system_argv() helper for safer calls to
+ system utilities
+
+Often file names contain special characters like hashes or
+whitespaces and that makes escaping a difficult task when using
+system(). Thus add a new helper system_argv() that is based
+on execvp() syscall and doesn't require escaping.
+
+Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
+
+Upstream-Status: Backport (v2.54+)
+
+---
+ include/swupd.h | 1 +
+ src/fullfiles.c | 6 ++----
+ src/helpers.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 66 insertions(+), 4 deletions(-)
+
+diff --git a/include/swupd.h b/include/swupd.h
+index 00b88d4..2805186 100644
+--- a/include/swupd.h
++++ b/include/swupd.h
+@@ -223,6 +223,7 @@ extern FILE * fopen_exclusive(const char *filename); /* no mode, opens for write
+ extern void dump_file_info(struct file *file);
+ extern void string_or_die(char **strp, const char *fmt, ...);
+ extern void print_elapsed_time(struct timeval *previous_time, struct timeval *current_time);
++extern int system_argv(char *const argv[]);
+
+ extern bool signature_initialize(void);
+ extern void signature_terminate(void);
+diff --git a/src/fullfiles.c b/src/fullfiles.c
+index 1bb581e..fa78293 100644
+--- a/src/fullfiles.c
++++ b/src/fullfiles.c
+@@ -138,12 +138,10 @@ static void create_fullfile(struct file *file)
+ string_or_die(&tempfile, "%s/%s", empty, file->hash);
+ if (link(origin, tempfile) < 0) {
+ LOG(NULL, "hardlink failed", "%s due to %s (%s -> %s)", file->filename, strerror(errno), origin, tempfile);
+- string_or_die(&tarcommand, "cp -a %s %s", origin, tempfile);
+- if (system(tarcommand) != 0) {
+- LOG(NULL, "Failed to run command:", "%s", tarcommand);
++ char *const argv[] = {"cp", "-a", origin, tempfile, NULL};
++ if (system_argv(argv) != 0) {
+ assert(0);
+ }
+- free(tarcommand);
+ }
+
+ /* step 2a: tar it with each compression type */
+diff --git a/src/helpers.c b/src/helpers.c
+index 65acffd..5884b51 100644
+--- a/src/helpers.c
++++ b/src/helpers.c
+@@ -29,6 +29,7 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/time.h>
++#include <sys/wait.h>
+ #include <fcntl.h>
+ #include <errno.h>
+
+@@ -124,3 +125,65 @@ void print_elapsed_time(struct timeval *previous_time, struct timeval *current_t
+
+ free(elapsed);
+ }
++
++void concat_str_array(char **output, char *const argv[])
++{
++ int size = 0;
++
++ for (int i = 0; argv[i]; i++) {
++ size += strlen(argv[i]) + 1;
++ }
++
++ *output = malloc(size + 1);
++ if (!*output) {
++ LOG(NULL, "Failed to allocate", "%i bytes", size);
++ assert(0);
++ }
++ strcpy(*output, "");
++ for (int i = 0; argv[i]; i++) {
++ strcat(*output, argv[i]);
++ strcat(*output, " ");
++ }
++}
++
++int system_argv(char *const argv[])
++{
++ int child_exit_status;
++ pid_t pid;
++ int status;
++
++ pid = fork();
++
++ if (pid == 0) { /* child */
++ execvp(*argv, argv);
++ LOG(NULL, "This line must not be reached", "");
++ assert(0);
++ } else if (pid < 0) {
++ LOG(NULL, "Failed to fork a child process", "");
++ assert(0);
++ } else {
++ pid_t ws = waitpid(pid, &child_exit_status, 0);
++
++ if (ws == -1) {
++ LOG(NULL, "Failed to wait for child process", "");
++ assert(0);
++ }
++
++ if (WIFEXITED(child_exit_status)) {
++ status = WEXITSTATUS(child_exit_status);
++ } else {
++ LOG(NULL, "Child process didn't exit", "");
++ assert(0);
++ }
++
++ if (status != 0) {
++ char* cmdline = NULL;
++
++ concat_str_array(&cmdline, argv);
++ LOG(NULL, "Failed to run command:", "%s", cmdline);
++ free(cmdline);
++ }
++
++ return status;
++ }
++}
+--
+2.5.0
+
diff --git a/recipes-core/swupd-server/swupd-server/0002-Add-configure-option-to-re-enable-config-files-in-ma.patch b/recipes-core/swupd-server/swupd-server/0002-Add-configure-option-to-re-enable-config-files-in-ma.patch
new file mode 100644
index 0000000..a5ab7af
--- /dev/null
+++ b/recipes-core/swupd-server/swupd-server/0002-Add-configure-option-to-re-enable-config-files-in-ma.patch
@@ -0,0 +1,66 @@
+From e209255d8985eb4eb4c43f5e773ee1a0a16eb297 Mon Sep 17 00:00:00 2001
+From: Joshua Lock <joshua.g.lock@intel.com>
+Date: Fri, 12 Feb 2016 16:02:58 +0000
+Subject: [PATCH 2/2] Add configure option to re-enable config files in
+ manifests
+
+Signed-off-by: Joshua Lock <joshua.g.lock@intel.com>
+
+Upstream-Status: Accepted (v3.0+)
+
+---
+ configure.ac | 6 ++++++
+ include/swupd.h | 6 ++++++
+ src/manifest.c | 2 +-
+ 3 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 8bb6e3b..afffad5 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -28,6 +28,12 @@ AC_ARG_ENABLE(
+ AC_DEFINE(SWUPD_WITH_LZMA,1,[Use lzma compression])
+ [enable_lzma=check]
+ )
++AC_ARG_ENABLE(
++ [stateless],
++ AS_HELP_STRING([--disable-stateless],[OS is not stateless, do not ignore configuration files (stateless by default)]),
++ AC_DEFINE(SWUPD_WITH_STATELESS,0,[OS is not stateless]),
++ AC_DEFINE(SWUPD_WITH_STATELESS,1,[OS is stateless])
++)
+
+ AS_IF([test "$enable_lzma" = "check"],
+ [PKG_CHECK_MODULES([lzma],
+diff --git a/include/swupd.h b/include/swupd.h
+index 58307d9..7cce735 100644
+--- a/include/swupd.h
++++ b/include/swupd.h
+@@ -23,6 +23,12 @@
+ #define TAR_PERM_ATTR_ARGS "--preserve-permissions --xattrs --xattrs-include='*'"
+ #endif
+
++#if SWUPD_WITH_STATELESS
++#define OS_IS_STATELESS 1
++#else
++#define OS_IS_STATELESS 0
++#endif
++
+ /* Build types */
+ #define REGULAR_BUILD 0
+ #define FIX_BUILD 1
+diff --git a/src/manifest.c b/src/manifest.c
+index 69a8fb9..58d0be1 100644
+--- a/src/manifest.c
++++ b/src/manifest.c
+@@ -830,7 +830,7 @@ int prune_manifest(struct manifest *manifest)
+ next = g_list_next(list);
+ file = list->data;
+
+- if ((!file->is_deleted) && (file->is_config)) {
++ if (OS_IS_STATELESS && (!file->is_deleted) && (file->is_config)) {
+ // toward being a stateless OS
+ LOG(file, "Skipping config file in manifest write", "component %s", manifest->component);
+ manifest->files = g_list_delete_link(manifest->files, list);
+--
+2.5.0
+
diff --git a/recipes-core/swupd-server/swupd-server/0002-Fix-regression-that-introduced-a-directory-named.patch b/recipes-core/swupd-server/swupd-server/0002-Fix-regression-that-introduced-a-directory-named.patch
new file mode 100644
index 0000000..9c1d8fa
--- /dev/null
+++ b/recipes-core/swupd-server/swupd-server/0002-Fix-regression-that-introduced-a-directory-named.patch
@@ -0,0 +1,29 @@
+From aba4418696ef31ab5afc9811903a044366619dc4 Mon Sep 17 00:00:00 2001
+From: Patrick McCarty <patrick.mccarty@intel.com>
+Date: Mon, 22 Feb 2016 22:07:08 -0800
+Subject: [PATCH 2/4] Fix regression that introduced a directory named '/ '
+
+Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
+
+Upstream-Status: Backport (v3.0+)
+
+---
+ src/chroot.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/chroot.c b/src/chroot.c
+index f961d58..2878496 100644
+--- a/src/chroot.c
++++ b/src/chroot.c
+@@ -40,7 +40,7 @@ void chroot_create_full(int newversion)
+ char * command;
+ char *full_dir;
+
+- string_or_die(&full_dir, "%s/%i/full/ ", image_dir, newversion);
++ string_or_die(&full_dir, "%s/%i/full/", image_dir, newversion);
+
+ g_mkdir_with_parents(full_dir, S_IRWXU);
+
+--
+2.5.0
+
diff --git a/recipes-core/swupd-server/swupd-server_2.53.bb b/recipes-core/swupd-server/swupd-server_2.53.bb
new file mode 100644
index 0000000..fdbd39b
--- /dev/null
+++ b/recipes-core/swupd-server/swupd-server_2.53.bb
@@ -0,0 +1,33 @@
+SUMMARY = "swupd sofware update from Clear Linux - server component"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://COPYING;md5=04d0b48662817042d80393e7511fa41b \
+ file://bsdiff/LICENSE;md5=0dbe7a50f028269750631fcbded3846a"
+
+DEPENDS = "file xz glib-2.0 zlib bzip2 tar rsync openssl"
+
+SRC_URI = "\
+ https://download.clearlinux.org/releases/5940/clear/source/SRPMS/${BPN}-${PV}-4.src.rpm;extract=${BP}.tar.gz \
+ file://0001-Add-option-S-to-take-the-state-data-dir-as-an-argume.patch \
+ file://0001-Add-system_argv-helper-for-safer-calls-to-system-uti.patch \
+ file://0002-Add-configure-option-to-re-enable-config-files-in-ma.patch \
+ file://0002-Fix-regression-that-introduced-a-directory-named.patch \
+"
+
+SRC_URI[md5sum] = "14f25677b5a4f0b33785910b03860939"
+SRC_URI[sha256sum] = "c2d0e595444fe198c4092dd83d20a929fd1402a13b66b410b76677ed3a993d99"
+
+inherit autotools
+
+EXTRA_OECONF = "--enable-bzip2 --enable-lzma --disable-stateless"
+
+# safer-calls-to-system-utilities.patch uses for loop initial declaration
+CFLAGS_append = " -std=c99"
+
+do_install_append () {
+ mkdir -p ${D}${sysconfdir}/swupd-certs
+ install -m 0755 ${S}/test/signature/* ${D}${sysconfdir}/swupd-certs/
+}
+
+RDEPENDS_${PN} = "tar rsync"
+
+BBCLASSEXTEND = "native"