aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-core
diff options
context:
space:
mode:
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"