auc: remove package
authorDaniel Golle <daniel@makrotopia.org>
Tue, 25 Jun 2024 16:10:01 +0000 (17:10 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Wed, 26 Jun 2024 02:07:01 +0000 (03:07 +0100)
With 'owut' there is now a much better alternative available.
Retire and remove 'auc' in favor of 'owut'.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
utils/auc/Makefile [deleted file]
utils/auc/src/CMakeLists.txt [deleted file]
utils/auc/src/auc.c [deleted file]

diff --git a/utils/auc/Makefile b/utils/auc/Makefile
deleted file mode 100644 (file)
index bec42a1..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=auc
-PKG_VERSION:=0.3.2
-PKG_RELEASE:=1
-PKG_LICENSE:=GPL-3.0
-
-include $(INCLUDE_DIR)/package.mk
-include $(INCLUDE_DIR)/cmake.mk
-
-define Package/auc
-  SECTION:=base
-  CATEGORY:=Base system
-  TITLE:=Attended sysUpgrade CLI (EXPERIMENTAL)
-  DEPENDS:=+attendedsysupgrade-common +libblobmsg-json +libubox +libubus \
-           +libuci +libuclient +rpcd-mod-rpcsys
-endef
-
-define Package/auc/description
-   CLI client for attended-sysupgrade
-endef
-
-EXTRA_CFLAGS += \
-       -D'AUC_VERSION=\"$(PKG_VERSION)-$(PKG_RELEASE)\"' \
-       $(if $(CONFIG_DEBUG),-DAUC_DEBUG=ON)
-
-define Package/auc/install
-       $(INSTALL_DIR) $(1)/usr/sbin
-       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/auc $(1)/usr/sbin/
-endef
-
-$(eval $(call BuildPackage,auc))
diff --git a/utils/auc/src/CMakeLists.txt b/utils/auc/src/CMakeLists.txt
deleted file mode 100644 (file)
index 14cc199..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-cmake_minimum_required(VERSION 2.6...3.12)
-
-PROJECT(auc C)
-ADD_DEFINITIONS(-Os -ggdb -Wall --std=gnu99 -Wmissing-declarations)
-
-SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
-
-find_library(json NAMES json-c json)
-
-ADD_EXECUTABLE(auc auc.c)
-TARGET_LINK_LIBRARIES(auc uci ubox ubus uclient blobmsg_json ${json} ${CMAKE_DL_LIBS})
-INSTALL(TARGETS auc RUNTIME DESTINATION sbin)
diff --git a/utils/auc/src/auc.c b/utils/auc/src/auc.c
deleted file mode 100644 (file)
index 43940ff..0000000
+++ /dev/null
@@ -1,2196 +0,0 @@
-/*
- * auc - attendedsysUpgrade CLI
- * Copyright (C) 2017-2021 Daniel Golle <daniel@makrotopia.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#define _GNU_SOURCE
-#ifndef AUC_VERSION
-#define AUC_VERSION "unknown"
-#endif
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <glob.h>
-#include <stdio.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <stdbool.h>
-
-#include <uci.h>
-#include <uci_blob.h>
-#include <json-c/json.h>
-#include <libubox/ulog.h>
-#include <libubox/list.h>
-#include <libubox/vlist.h>
-#include <libubox/blobmsg_json.h>
-#include <libubox/avl-cmp.h>
-#include <libubox/uclient.h>
-#include <libubox/uclient-utils.h>
-#include <libubus.h>
-
-#define REQ_TIMEOUT 15
-
-#define API_BRANCHES "branches"
-#define API_INDEX "index"
-#define API_JSON "json"
-#define API_JSON_VERSION "v1"
-#define API_JSON_EXT "." API_JSON
-#define API_PACKAGES "packages"
-#define API_REQUEST "api/v1/build"
-#define API_STATUS_QUEUED "queued"
-#define API_STATUS_STARTED "started"
-#define API_STORE "store"
-#define API_TARGETS "targets"
-
-#define PUBKEY_PATH "/etc/opkg/keys"
-#define SHA256SUM "/bin/busybox sha256sum"
-
-#ifdef AUC_DEBUG
-#define DPRINTF(...) if (debug) fprintf(stderr, __VA_ARGS__)
-#else
-#define DPRINTF(...)
-#endif
-
-static const char server_issues[]="https://github.com/openwrt/asu/issues";
-
-static struct ubus_context *ctx;
-static struct uclient *ucl = NULL;
-static char user_agent[80];
-static char *serverurl;
-static int upgrade_packages;
-static struct ustream_ssl_ctx *ssl_ctx;
-static const struct ustream_ssl_ops *ssl_ops;
-static off_t out_bytes;
-static off_t out_len;
-static off_t out_offset;
-static bool cur_resume;
-static int output_fd = -1;
-static bool retry = false;
-static char *board_name = NULL;
-static char *target = NULL;
-static char *distribution = NULL, *version = NULL, *revision = NULL;
-static char *rootfs_type = NULL;
-static int uptodate;
-static char *filename = NULL;
-static void *dlh = NULL;
-static int rc;
-static bool dont_ask = false;
-
-static int avl_verrevcmp(const void *k1, const void *k2, void *ptr);
-
-struct branch {
-       struct avl_node avl;
-       char *name;
-       char *git_branch;
-       char *path_packages;
-       char *arch_packages;
-       char **repos;
-       struct avl_tree versions;
-       struct list_head package_changes;
-       bool snapshot;
-       unsigned int branch_off_rev;
-};
-static struct avl_tree branches = AVL_TREE_INIT(branches, avl_verrevcmp, false, NULL);
-
-struct branch_version {
-       struct avl_node avl;
-       struct branch *branch;
-       char *path;
-       char *version;
-       char *version_code;
-       char *version_number;
-       bool snapshot;
-};
-
-struct package_changes {
-       struct list_head list;
-       unsigned int revision;
-       char *source;
-       char *target;
-       bool mandatory;
-};
-static LIST_HEAD(selected_package_changes);
-
-struct avl_pkg {
-       struct avl_node avl;
-       char *name;
-       char *version;
-};
-static struct avl_tree pkg_tree = AVL_TREE_INIT(pkg_tree, avl_strcmp, false, NULL);
-
-#ifdef AUC_DEBUG
-static int debug = 0;
-#endif
-
-/*
- * policy for ubus call system board
- * see procd/system.c
- */
-enum {
-       BOARD_BOARD_NAME,
-       BOARD_RELEASE,
-       BOARD_ROOTFS_TYPE,
-       __BOARD_MAX,
-};
-
-static const struct blobmsg_policy board_policy[__BOARD_MAX] = {
-       [BOARD_BOARD_NAME] = { .name = "board_name", .type = BLOBMSG_TYPE_STRING },
-       [BOARD_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_TABLE },
-       [BOARD_ROOTFS_TYPE] = { .name = "rootfs_type", .type = BLOBMSG_TYPE_STRING },
-};
-
-/*
- * policy for release information in system board reply
- * see procd/system.c
- */
-enum {
-       RELEASE_DISTRIBUTION,
-       RELEASE_REVISION,
-       RELEASE_TARGET,
-       RELEASE_VERSION,
-       __RELEASE_MAX,
-};
-
-static const struct blobmsg_policy release_policy[__RELEASE_MAX] = {
-       [RELEASE_DISTRIBUTION] = { .name = "distribution", .type = BLOBMSG_TYPE_STRING },
-       [RELEASE_REVISION] = { .name = "revision", .type = BLOBMSG_TYPE_STRING },
-       [RELEASE_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
-       [RELEASE_VERSION] = { .name = "version", .type = BLOBMSG_TYPE_STRING },
-};
-
-/*
- * policy for package list returned from rpc-sys or from server
- * see rpcd/sys.c and ASU sources
- */
-enum {
-       PACKAGES_ARCHITECTURE,
-       PACKAGES_PACKAGES,
-       __PACKAGES_MAX,
-};
-
-static const struct blobmsg_policy packages_policy[__PACKAGES_MAX] = {
-       [PACKAGES_ARCHITECTURE] = { .name = "architecture", .type = BLOBMSG_TYPE_STRING },
-       [PACKAGES_PACKAGES] = { .name = "packages", .type = BLOBMSG_TYPE_TABLE },
-};
-
-/*
- * policy for upgrade_test
- * see rpcd/sys.c
- */
-enum {
-       UPGTEST_CODE,
-       UPGTEST_STDERR,
-       __UPGTEST_MAX,
-};
-
-static const struct blobmsg_policy upgtest_policy[__UPGTEST_MAX] = {
-       [UPGTEST_CODE] = { .name = "code", .type = BLOBMSG_TYPE_INT32 },
-       [UPGTEST_STDERR] = { .name = "stderr", .type = BLOBMSG_TYPE_STRING },
-};
-
-/*
- * policy for branches.json
- */
-enum {
-       BRANCH_ENABLED,
-       BRANCH_GIT_BRANCH,
-       BRANCH_BRANCH_OFF_REV,
-       BRANCH_NAME,
-       BRANCH_PATH,
-       BRANCH_PATH_PACKAGES,
-       BRANCH_SNAPSHOT,
-       BRANCH_REPOS,
-       BRANCH_TARGETS,
-       BRANCH_UPDATES,
-       BRANCH_VERSIONS,
-       BRANCH_PACKAGE_CHANGES,
-       __BRANCH_MAX,
-};
-
-static const struct blobmsg_policy branches_policy[__BRANCH_MAX] = {
-       [BRANCH_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
-       [BRANCH_GIT_BRANCH] = { .name = "git_branch", .type = BLOBMSG_TYPE_STRING },
-       [BRANCH_BRANCH_OFF_REV] = { .name = "branch_off_rev", .type = BLOBMSG_TYPE_INT32 },
-       [BRANCH_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
-       [BRANCH_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
-       [BRANCH_PATH_PACKAGES] = { .name = "path_packages", .type = BLOBMSG_TYPE_STRING },
-       [BRANCH_SNAPSHOT] = { .name = "snapshot", .type = BLOBMSG_TYPE_BOOL },
-       [BRANCH_REPOS] = { .name = "repos", .type = BLOBMSG_TYPE_ARRAY },
-       [BRANCH_TARGETS] = { .name = "targets", .type = BLOBMSG_TYPE_TABLE },
-       [BRANCH_UPDATES] = { .name = "updates", .type = BLOBMSG_TYPE_STRING },
-       [BRANCH_VERSIONS] = { .name = "versions", .type = BLOBMSG_TYPE_ARRAY },
-       [BRANCH_PACKAGE_CHANGES] = { .name = "package_changes", .type = BLOBMSG_TYPE_ARRAY },
-};
-
-enum {
-       PACKAGE_CHANGES_SOURCE,
-       PACKAGE_CHANGES_TARGET,
-       PACKAGE_CHANGES_REVISION,
-       PACKAGE_CHANGES_MANDATORY,
-       __PACKAGE_CHANGES_MAX,
-};
-
-static const struct blobmsg_policy package_changes_policy[__PACKAGE_CHANGES_MAX] = {
-       [PACKAGE_CHANGES_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_STRING },
-       [PACKAGE_CHANGES_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
-       [PACKAGE_CHANGES_REVISION] = { .name = "revision", .type = BLOBMSG_TYPE_INT32 },
-       [PACKAGE_CHANGES_MANDATORY] = { .name = "mandatory", .type = BLOBMSG_TYPE_BOOL },
-};
-
-/*
- * shared policy for target.json and server image request reply
- */
-enum {
-       TARGET_ARCH_PACKAGES,
-       TARGET_BINDIR,
-       TARGET_DEVICE_PACKAGES,
-       TARGET_ENQUEUED_AT,
-       TARGET_IMAGES,
-       TARGET_DETAIL,
-       TARGET_MANIFEST,
-       TARGET_METADATA_VERSION,
-       TARGET_REQUEST_HASH,
-       TARGET_QUEUE_POSITION,
-       TARGET_STATUS,
-       TARGET_STDERR,
-       TARGET_STDOUT,
-       TARGET_TARGET,
-       TARGET_TITLES,
-       TARGET_VERSION_CODE,
-       TARGET_VERSION_NUMBER,
-       __TARGET_MAX,
-};
-
-static const struct blobmsg_policy target_policy[__TARGET_MAX] = {
-       [TARGET_ARCH_PACKAGES] = { .name = "arch_packages", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_BINDIR] = { .name = "bin_dir", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_DEVICE_PACKAGES] = { .name = "device_packages", .type = BLOBMSG_TYPE_ARRAY },
-       [TARGET_ENQUEUED_AT] = { .name = "enqueued_at", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_IMAGES] = { .name = "images", .type = BLOBMSG_TYPE_ARRAY },
-       [TARGET_MANIFEST] = { .name = "manifest", .type = BLOBMSG_TYPE_TABLE },
-       [TARGET_DETAIL] = { .name = "detail", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_METADATA_VERSION] = { .name = "metadata_version", .type = BLOBMSG_TYPE_INT32 },
-       [TARGET_REQUEST_HASH] = { .name = "request_hash", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_QUEUE_POSITION] = { .name = "queue_position", .type = BLOBMSG_TYPE_INT32 },
-       [TARGET_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_STDERR] = { .name = "stderr", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_STDOUT] = { .name = "stdout", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_TITLES] = { .name = "titles", .type = BLOBMSG_TYPE_ARRAY },
-       [TARGET_VERSION_CODE] = { .name = "version_code", .type = BLOBMSG_TYPE_STRING },
-       [TARGET_VERSION_NUMBER] = { .name = "version_number", .type = BLOBMSG_TYPE_STRING },
-};
-
-/*
- * policy for images object in target
- */
-enum {
-       IMAGES_FILESYSTEM,
-       IMAGES_NAME,
-       IMAGES_SHA256,
-       IMAGES_TYPE,
-       __IMAGES_MAX,
-};
-
-static const struct blobmsg_policy images_policy[__IMAGES_MAX] = {
-       [IMAGES_FILESYSTEM] = { .name = "filesystem", .type = BLOBMSG_TYPE_STRING },
-       [IMAGES_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
-       [IMAGES_SHA256] = { .name = "sha256", .type = BLOBMSG_TYPE_STRING },
-       [IMAGES_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
-};
-
-/*
- * generic policy for HTTP JSON reply
- */
-enum {
-       REPLY_ARRAY,
-       REPLY_OBJECT,
-       __REPLY_MAX,
-};
-
-static const struct blobmsg_policy reply_policy[__REPLY_MAX] = {
-       [REPLY_ARRAY] = { .name = "reply", .type = BLOBMSG_TYPE_ARRAY },
-       [REPLY_OBJECT] = { .name = "reply", .type = BLOBMSG_TYPE_TABLE },
-};
-
-/*
- * policy for HTTP headers received from server
- */
-enum {
-       H_LEN,
-       H_RANGE,
-       H_UNKNOWN_PACKAGE,
-       H_QUEUE_POSITION,
-       __H_MAX
-};
-
-static const struct blobmsg_policy header_policy[__H_MAX] = {
-       [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING },
-       [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
-       [H_UNKNOWN_PACKAGE] = { .name = "x-unknown-package", .type = BLOBMSG_TYPE_STRING },
-       [H_QUEUE_POSITION] = { .name = "x-queue-position", .type = BLOBMSG_TYPE_INT32 },
-};
-
-/*
- * load serverurl from UCI
- */
-static int load_config() {
-       struct uci_context *uci_ctx;
-       struct uci_package *uci_attendedsysupgrade;
-       struct uci_section *uci_s;
-       char *url;
-
-       uci_ctx = uci_alloc_context();
-       if (!uci_ctx)
-               return -1;
-
-       uci_ctx->flags &= ~UCI_FLAG_STRICT;
-
-       if (uci_load(uci_ctx, "attendedsysupgrade", &uci_attendedsysupgrade) ||
-           !uci_attendedsysupgrade) {
-               fprintf(stderr, "Failed to load attendedsysupgrade config\n");
-               return -1;
-       }
-       uci_s = uci_lookup_section(uci_ctx, uci_attendedsysupgrade, "server");
-       if (!uci_s) {
-               fprintf(stderr, "Failed to read server config section\n");
-               return -1;
-       }
-       url = uci_lookup_option_string(uci_ctx, uci_s, "url");
-       if (!url) {
-               fprintf(stderr, "Failed to read server url from config\n");
-               return -1;
-       }
-       if (strncmp(url, "https://", strlen("https://")) &&
-           strncmp(url, "http://", strlen("http://"))) {
-               fprintf(stderr, "Server url invalid (needs to be http://... or https://...)\n");
-               return -1;
-       }
-
-       serverurl = strdup(url);
-
-       uci_s = uci_lookup_section(uci_ctx, uci_attendedsysupgrade, "client");
-       if (!uci_s) {
-               fprintf(stderr, "Failed to read client config\n");
-               return -1;
-       }
-       upgrade_packages = atoi(uci_lookup_option_string(uci_ctx, uci_s, "upgrade_packages"));
-
-       uci_free_context(uci_ctx);
-
-       return 0;
-}
-
-/*
- * libdpkg - Debian packaging suite library routines
- * vercmp.c - comparison of version numbers
- *
- * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
- */
-
-/* assume ascii; warning: evaluates x multiple times! */
-#define order(x) ((x) == '~' ? -1 \
-               : isdigit((x)) ? 0 \
-               : !(x) ? 0 \
-               : isalpha((x)) ? (x) \
-               : (x) + 256)
-
-static int verrevcmp(const char *val, const char *ref)
-{
-       if (!val)
-               val = "";
-       if (!ref)
-               ref = "";
-
-       while (*val || *ref) {
-               int first_diff = 0;
-
-               while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) {
-                       int vc = order(*val), rc = order(*ref);
-                       if (vc != rc)
-                               return vc - rc;
-                       val++;
-                       ref++;
-               }
-
-               while (*val == '0')
-                       val++;
-               while (*ref == '0')
-                       ref++;
-               while (isdigit(*val) && isdigit(*ref)) {
-                       if (!first_diff)
-                               first_diff = *val - *ref;
-                       val++;
-                       ref++;
-               }
-               if (isdigit(*val))
-                       return 1;
-               if (isdigit(*ref))
-                       return -1;
-               if (first_diff)
-                       return first_diff;
-       }
-       return 0;
-}
-
-static int avl_verrevcmp(const void *k1, const void *k2, void *ptr)
-{
-       const char *d1 = (const char *)k1, *d2 = (const char*)k2;
-
-       return verrevcmp(d1, d2);
-}
-
-/*
- * replace '-rc' by '~' in string
- */
-static inline void release_replace_rc(char *ver)
-{
-       char *tmp;
-
-       tmp = strstr(ver, "-rc");
-       if (tmp && strlen(tmp) > 3) {
-               *tmp = '~';
-               memmove(tmp + 1, tmp + 3, strlen(tmp + 3) + 1);
-       }
-}
-
-/*
- * OpenWrt release version string comperator
- * replaces '-rc' by '~' to fix ordering of release(s) (candidates)
- * using the void release_replace_rc(char *ver) function above.
- */
-static int openwrt_release_verrevcmp(const char *ver1, const char *ver2)
-{
-       char mver1[16], mver2[16];
-
-       strncpy(mver1, ver1, sizeof(mver1) - 1);
-       mver1[sizeof(mver1) - 1] = '\0';
-       strncpy(mver2, ver2, sizeof(mver2) - 1);
-       mver2[sizeof(mver2) - 1] = '\0';
-
-       release_replace_rc(mver1);
-       release_replace_rc(mver2);
-
-       return verrevcmp(mver1, mver2);
-}
-
-/*
- * Select package_changes from branch to global list
- */
-static void grab_changes(struct branch *br, unsigned int rev)
-{
-       struct package_changes *c, *n;
-
-#ifdef AUC_DEBUG
-       if (debug)
-               fprintf(stderr, "grabbing changes for branch %s from revision %u\n", br->name, rev);
-#endif
-
-       list_for_each_entry(c, &br->package_changes, list) {
-               if (c->revision == 0 || c->revision > rev) {
-                       n = malloc(sizeof(struct package_changes));
-                       memcpy(n, c, sizeof(struct package_changes));
-                       INIT_LIST_HEAD(&n->list);
-                       list_add_tail(&n->list, &selected_package_changes);
-               }
-       }
-}
-
-/**
- * UBUS response callbacks
- */
-/*
- * rpc-sys packagelist
- * append array of package names to blobbuf given in req->priv
- */
-#define ANSI_ESC "\x1b"
-#define ANSI_COLOR_RESET ANSI_ESC "[0m"
-#define ANSI_COLOR_RED ANSI_ESC "[1;31m"
-#define ANSI_COLOR_GREEN ANSI_ESC "[1;32m"
-#define ANSI_COLOR_BLUE ANSI_ESC "[1;34m"
-#define ANSI_CURSOR_SAFE "[s"
-#define ANSI_CURSOR_RESTORE "[u"
-#define ANSI_ERASE_LINE "[K"
-
-#define PKG_UPGRADE 0x1
-#define PKG_DOWNGRADE 0x2
-#define PKG_NOT_FOUND 0x4
-#define PKG_ERROR 0x8
-
-static bool ask_user(const char *message)
-{
-       char user_input;
-       fflush(stdin);
-       fprintf(stderr, "%s [N/y] ", message);
-       user_input = getchar();
-       fflush(stdin);
-       if ((user_input == 'y') || (user_input == 'Y'))
-               return true;
-
-       return false;
-}
-
-static inline bool is_builtin_pkg(const char *pkgname)
-{
-       return !strcmp(pkgname, "libc") ||
-               !strcmp(pkgname, "librt") ||
-               !strcmp(pkgname, "libpthread") ||
-               !strcmp(pkgname, "kernel");
-}
-
-static const char *apply_package_changes(const char *pkgname, bool interactive)
-{
-       struct package_changes *pkc;
-       const char *mpkgname = pkgname;
-
-       list_for_each_entry(pkc, &selected_package_changes, list) {
-               /* package_change additions are dealt with later */
-               if (!pkc->source)
-                       continue;
-
-               if (strcmp(pkc->source, mpkgname))
-                       continue;
-
-               if (!pkc->mandatory && interactive) {
-                       if (pkc->target)
-                               fprintf(stderr, "Package %s should be replaced by %s.\n", pkc->source, pkc->target);
-                       else
-                               fprintf(stderr, "Package %s should be removed.\n", pkc->source);
-
-                       if (dont_ask)
-                               pkc->mandatory = true;
-                       else
-                               pkc->mandatory = ask_user("Apply change");
-               }
-
-               if (!pkc->mandatory)
-                       continue;
-
-               mpkgname = pkc->target;
-
-               if (!mpkgname)
-                       break;
-       }
-       return mpkgname;
-}
-
-static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_attr *msg)
-{
-       int *status = (int *)req->priv;
-       struct blob_attr *tb[__PACKAGES_MAX], *cur;
-       struct avl_pkg *pkg;
-       int rem;
-       int cmpres;
-       const char *pkgname;
-       struct package_changes *pkc;
-
-       blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
-
-       if (!tb[PACKAGES_PACKAGES])
-               return;
-
-       blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) {
-               pkgname = blobmsg_name(cur);
-               if (is_builtin_pkg(pkgname))
-                       continue;
-
-               pkgname = apply_package_changes(pkgname, true);
-               if (!pkgname) {
-                       fprintf(stderr, " %s: %s%s -> (not installed)%s\n",
-                               blobmsg_name(cur), ANSI_COLOR_BLUE,
-                               blobmsg_get_string(cur), ANSI_COLOR_RESET);
-                       continue;
-               }
-
-               pkg = avl_find_element(&pkg_tree, pkgname, pkg, avl);
-               if (!pkg) {
-                       fprintf(stderr, "installed package %s%s%s cannot be found in remote list!\n",
-                               ANSI_COLOR_RED, pkgname, ANSI_COLOR_RESET);
-                       *status |= PKG_NOT_FOUND;
-                       continue;
-               }
-
-               if (pkgname != blobmsg_name(cur)) {
-                       fprintf(stderr, " %s%s: %s -> %s: %s%s\n", ANSI_COLOR_BLUE,
-                               blobmsg_name(cur),
-                               blobmsg_get_string(cur), pkgname, pkg->version,
-                               ANSI_COLOR_RESET);
-                       *status |= PKG_UPGRADE;
-                       continue;
-               }
-
-               cmpres = verrevcmp(blobmsg_get_string(cur), pkg->version);
-               if (cmpres < 0)
-                       *status |= PKG_UPGRADE;
-
-               if (cmpres > 0)
-                       *status |= PKG_DOWNGRADE;
-
-               if (cmpres
-#ifdef AUC_DEBUG
-               || debug
-#endif
-                       )
-                       fprintf(stderr, " %s: %s%s -> %s%s\n", blobmsg_name(cur),
-                               (!cmpres)?"":(cmpres > 0)?ANSI_COLOR_RED:ANSI_COLOR_GREEN,
-                               blobmsg_get_string(cur), pkg->version,
-                               (cmpres)?ANSI_COLOR_RESET:"");
-       }
-
-       list_for_each_entry(pkc, &selected_package_changes, list) {
-               /* deal only with package_change additions now */
-               if (pkc->source)
-                       continue;
-
-               pkg = avl_find_element(&pkg_tree, pkc->target, pkg, avl);
-               if (!pkg) {
-                       fprintf(stderr, "new package %s%s%s cannot be found in remote list!\n",
-                               ANSI_COLOR_RED, pkc->target, ANSI_COLOR_RESET);
-                       *status |= PKG_NOT_FOUND;
-                       continue;
-               }
-               fprintf(stderr, " %s: %s(not installed) -> %s%s\n", pkc->target, ANSI_COLOR_BLUE,
-                       pkg->version, ANSI_COLOR_RESET);
-       }
-}
-
-/*
- * rpc-sys packagelist
- * append array of package names to blobbuf given in req->priv
- */
-static void pkglist_req_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
-       struct blob_buf *buf = (struct blob_buf *)req->priv;
-       struct blob_attr *tb[__PACKAGES_MAX];
-       struct blob_attr *cur;
-       int rem;
-       struct avl_pkg *pkg;
-       void *table;
-       const char *pkgname;
-       struct package_changes *pkc;
-
-       blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blob_data(msg), blob_len(msg));
-
-       if (!tb[PACKAGES_PACKAGES]) {
-               fprintf(stderr, "No packagelist received\n");
-               return;
-       }
-
-       table = blobmsg_open_table(buf, "packages_versions");
-
-       blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) {
-               pkgname = blobmsg_name(cur);
-               if (is_builtin_pkg(pkgname))
-                       continue;
-
-               pkgname = apply_package_changes(pkgname, false);
-               pkg = avl_find_element(&pkg_tree, pkgname, pkg, avl);
-               if (!pkg)
-                       continue;
-
-               blobmsg_add_string(buf, pkgname, pkg->version);
-       }
-
-       list_for_each_entry(pkc, &selected_package_changes, list) {
-               /* add new packages to request */
-               if (pkc->source)
-                       continue;
-
-               pkg = avl_find_element(&pkg_tree, pkc->target, pkg, avl);
-               if (!pkg)
-                       continue;
-
-               blobmsg_add_string(buf, pkc->target, pkg->version);
-       }
-       blobmsg_close_table(buf, table);
-};
-
-/*
- * system board
- * append append board information to blobbuf given in req->priv
- * populate board and release global strings
- */
-static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
-       struct blob_buf *buf = (struct blob_buf *)req->priv;
-       struct blob_attr *tb[__BOARD_MAX];
-       struct blob_attr *rel[__RELEASE_MAX];
-
-       blobmsg_parse(board_policy, __BOARD_MAX, tb, blob_data(msg), blob_len(msg));
-
-
-       if (!tb[BOARD_RELEASE]) {
-               fprintf(stderr, "No release received\n");
-               rc=-ENODATA;
-               return;
-       }
-
-       blobmsg_parse(release_policy, __RELEASE_MAX, rel,
-                       blobmsg_data(tb[BOARD_RELEASE]), blobmsg_data_len(tb[BOARD_RELEASE]));
-
-       if (!rel[RELEASE_TARGET] ||
-           !rel[RELEASE_DISTRIBUTION] ||
-           !rel[RELEASE_VERSION] ||
-           !rel[RELEASE_REVISION]) {
-               fprintf(stderr, "No release information received\n");
-               rc=-ENODATA;
-               return;
-       }
-
-       target = strdup(blobmsg_get_string(rel[RELEASE_TARGET]));
-       distribution = strdup(blobmsg_get_string(rel[RELEASE_DISTRIBUTION]));
-       version = strdup(blobmsg_get_string(rel[RELEASE_VERSION]));
-       revision = strdup(blobmsg_get_string(rel[RELEASE_REVISION]));
-
-       if (!strcmp(target, "x86/64") || !strcmp(target, "x86/generic")) {
-               /*
-                * ugly work-around ahead:
-                * ignore board name on generic x86 targets, as image name is always 'generic'
-                */
-               board_name = strdup("generic");
-       } else {
-               if (!tb[BOARD_BOARD_NAME]) {
-                       fprintf(stderr, "No board name received\n");
-                       rc=-ENODATA;
-                       return;
-               }
-               board_name = strdup(blobmsg_get_string(tb[BOARD_BOARD_NAME]));
-       }
-
-       if (tb[BOARD_ROOTFS_TYPE])
-               rootfs_type = strdup(blobmsg_get_string(tb[BOARD_ROOTFS_TYPE]));
-
-       blobmsg_add_string(buf, "target", target);
-       blobmsg_add_string(buf, "version", version);
-       blobmsg_add_string(buf, "revision", revision);
-}
-
-/*
- * rpc-sys upgrade_test
- * check if downloaded file is accepted by sysupgrade
- */
-static void upgtest_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
-       int *valid = (int *)req->priv;
-       struct blob_attr *tb[__UPGTEST_MAX];
-
-       blobmsg_parse(upgtest_policy, __UPGTEST_MAX, tb, blob_data(msg), blob_len(msg));
-
-       if (!tb[UPGTEST_CODE]) {
-               fprintf(stderr, "No sysupgrade test return code received\n");
-               return;
-       }
-
-       *valid = (blobmsg_get_u32(tb[UPGTEST_CODE]) == 0)?1:0;
-
-       if (tb[UPGTEST_STDERR])
-               fprintf(stderr, "%s", blobmsg_get_string(tb[UPGTEST_STDERR]));
-       else if (*valid == 0)
-               fprintf(stderr, "image verification failed\n");
-       else
-               fprintf(stderr, "image verification succeeded\n");
-};
-
-/**
- * uclient stuff
- */
-static int open_output_file(const char *path, uint64_t resume_offset)
-{
-       char *filename = NULL;
-       int flags;
-       int ret;
-
-       if (cur_resume)
-               flags = O_RDWR;
-       else
-               flags = O_WRONLY | O_EXCL;
-
-       flags |= O_CREAT;
-
-       filename = uclient_get_url_filename(path, "firmware.bin");
-
-       fprintf(stderr, "Writing to '%s'\n", filename);
-       ret = open(filename, flags, 0644);
-       if (ret < 0)
-               goto free;
-
-       if (resume_offset &&
-           lseek(ret, resume_offset, SEEK_SET) < 0) {
-               fprintf(stderr, "Failed to seek %"PRIu64" bytes in output file\n", resume_offset);
-               close(ret);
-               ret = -1;
-               goto free;
-       }
-
-       out_offset = resume_offset;
-       out_bytes += resume_offset;
-
-free:
-       free(filename);
-       return ret;
-}
-
-struct jsonblobber {
-       json_tokener *tok;
-       struct blob_buf *outbuf;
-};
-
-static void request_done(struct uclient *cl)
-{
-       struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
-       if (jsb) {
-               json_tokener_free(jsb->tok);
-               free(jsb);
-       };
-
-       uclient_disconnect(cl);
-       uloop_end();
-}
-
-static void header_done_cb(struct uclient *cl)
-{
-       struct blob_attr *tb[__H_MAX];
-       struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
-       struct blob_buf *outbuf = NULL;
-
-       if (jsb)
-               outbuf = jsb->outbuf;
-
-       uint64_t resume_offset = 0, resume_end, resume_size;
-
-       if (uclient_http_redirect(cl)) {
-               fprintf(stderr, "Redirected to %s on %s\n", cl->url->location, cl->url->host);
-
-               return;
-       }
-
-       if (cl->status_code == 204 && cur_resume) {
-               /* Resume attempt failed, try normal download */
-               cur_resume = false;
-               //init_request(cl);
-               return;
-       }
-
-       DPRINTF("status code: %d\n", cl->status_code);
-       DPRINTF("headers:\n%s\n", blobmsg_format_json_indent(cl->meta, true, 0));
-       blobmsg_parse(header_policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta));
-
-       switch (cl->status_code) {
-       case 400:
-               request_done(cl);
-               rc=-ESRCH;
-               break;
-       case 422:
-               fprintf(stderr, "unknown package '%s' requested.\n",
-                       blobmsg_get_string(tb[H_UNKNOWN_PACKAGE]));
-               rc=-ENOPKG;
-               request_done(cl);
-               break;
-       case 201:
-       case 202:
-               retry = true;
-               if (!outbuf)
-                       break;
-
-               blobmsg_add_u32(outbuf, "status", cl->status_code);
-
-               if (tb[H_QUEUE_POSITION])
-                       blobmsg_add_u32(outbuf, "queue_position", blobmsg_get_u32(tb[H_QUEUE_POSITION]));
-
-               break;
-       case 200:
-               retry = false;
-               if (cl->priv)
-                       break;
-
-               if (tb[H_LEN])
-                       out_len = strtoul(blobmsg_get_string(tb[H_LEN]), NULL, 10);
-
-               output_fd = open_output_file(cl->url->location, resume_offset);
-               if (output_fd < 0) {
-                       perror("Cannot open output file");
-                       request_done(cl);
-               }
-               break;
-       case 500:
-               /* server may reply JSON object */
-               break;
-
-       default:
-               DPRINTF("HTTP error %d\n", cl->status_code);
-               request_done(cl);
-               break;
-       }
-}
-
-static void read_data_cb(struct uclient *cl)
-{
-       char buf[256];
-       int len;
-       json_object *jsobj;
-       struct blob_buf *outbuf = NULL;
-       json_tokener *tok = NULL;
-       struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
-
-       if (!jsb) {
-               while (1) {
-                       len = uclient_read(cl, buf, sizeof(buf));
-                       if (!len)
-                               return;
-
-                       out_bytes += len;
-                       write(output_fd, buf, len);
-               }
-               return;
-       }
-
-       outbuf = jsb->outbuf;
-       tok = jsb->tok;
-
-       while (1) {
-               len = uclient_read(cl, buf, sizeof(buf));
-               if (!len)
-                       break;
-
-               out_bytes += len;
-
-               jsobj = json_tokener_parse_ex(tok, buf, len);
-
-               if (json_tokener_get_error(tok) == json_tokener_continue)
-                       continue;
-
-               if (json_tokener_get_error(tok) != json_tokener_success)
-                       break;
-
-               if (jsobj)
-               {
-                       blobmsg_add_json_element(outbuf, "reply", jsobj);
-
-                       json_object_put(jsobj);
-                       break;
-               }
-       }
-}
-
-static void eof_cb(struct uclient *cl)
-{
-       if (!cl->data_eof && !uptodate) {
-               fprintf(stderr, "Connection reset prematurely\n");
-       }
-       request_done(cl);
-}
-
-static void handle_uclient_error(struct uclient *cl, int code)
-{
-       const char *type = "Unknown error";
-
-       switch(code) {
-       case UCLIENT_ERROR_CONNECT:
-               type = "Connection failed";
-               break;
-       case UCLIENT_ERROR_TIMEDOUT:
-               type = "Connection timed out";
-               break;
-       case UCLIENT_ERROR_SSL_INVALID_CERT:
-               type = "Invalid SSL certificate";
-               break;
-       case UCLIENT_ERROR_SSL_CN_MISMATCH:
-               type = "Server hostname does not match SSL certificate";
-               break;
-       default:
-               break;
-       }
-
-       fprintf(stderr, "Connection error: %s\n", type);
-
-       request_done(cl);
-}
-
-static const struct uclient_cb check_cb = {
-       .header_done = header_done_cb,
-       .data_read = read_data_cb,
-       .data_eof = eof_cb,
-       .error = handle_uclient_error,
-};
-
-static int server_request(const char *url, struct blob_buf *inbuf, struct blob_buf *outbuf) {
-       struct jsonblobber *jsb = NULL;
-       int rc = -ENOENT;
-       char *post_data;
-       out_offset = 0;
-       out_bytes = 0;
-       out_len = 0;
-
-#ifdef AUC_DEBUG
-       if (debug)
-               fprintf(stderr, "Requesting URL: %s\n", url);
-#endif
-
-       if (outbuf) {
-               jsb = malloc(sizeof(struct jsonblobber));
-               jsb->outbuf = outbuf;
-               jsb->tok = json_tokener_new();
-       };
-
-       if (!ucl) {
-               ucl = uclient_new(url, NULL, &check_cb);
-               uclient_http_set_ssl_ctx(ucl, ssl_ops, ssl_ctx, 1);
-               ucl->timeout_msecs = REQ_TIMEOUT * 1000;
-       } else {
-               uclient_set_url(ucl, url, NULL);
-       }
-
-       ucl->priv = jsb;
-
-       rc = uclient_connect(ucl);
-       if (rc)
-               return rc;
-
-       rc = uclient_http_set_request_type(ucl, inbuf?"POST":"GET");
-       if (rc)
-               return rc;
-
-       uclient_http_reset_headers(ucl);
-       uclient_http_set_header(ucl, "User-Agent", user_agent);
-       if (inbuf) {
-               uclient_http_set_header(ucl, "Content-Type", "application/json");
-               post_data = blobmsg_format_json(inbuf->head, true);
-               uclient_write(ucl, post_data, strlen(post_data));
-       }
-       rc = uclient_request(ucl);
-       if (rc)
-               return rc;
-
-       uloop_run();
-
-       return 0;
-}
-
-/**
- * ustream-ssl
- */
-static int init_ustream_ssl(void) {
-       glob_t gl;
-       int i;
-
-       dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
-       if (!dlh)
-               return -ENOENT;
-
-       ssl_ops = dlsym(dlh, "ustream_ssl_ops");
-       if (!ssl_ops)
-               return -ENOENT;
-
-       ssl_ctx = ssl_ops->context_new(false);
-
-       glob("/etc/ssl/certs/*.crt", 0, NULL, &gl);
-       if (!gl.gl_pathc)
-               return -ENOKEY;
-
-       for (i = 0; i < gl.gl_pathc; i++)
-               ssl_ops->context_add_ca_crt_file(ssl_ctx, gl.gl_pathv[i]);
-
-       return 0;
-}
-
-static char* alloc_replace_var(char *in, const char *var, const char *replace)
-{
-       char *tmp = in;
-       char *res = NULL;
-       char *eptr;
-
-       while ((tmp = strchr(tmp, '{'))) {
-               ++tmp;
-               eptr = strchr(tmp, '}');
-               if (!eptr)
-                       return NULL;
-
-               if (!strncmp(tmp, var, (unsigned int)(eptr - tmp))) {
-                       asprintf(&res, "%.*s%s%s",
-                               (unsigned int)(tmp - in) - 1, in, replace, eptr + 1);
-                       break;
-               }
-       }
-
-       if (!res)
-               res = strdup(in);
-
-       return res;
-}
-
-static int request_target(struct branch_version *bver, char *url)
-{
-       static struct blob_buf boardbuf;
-       struct blob_attr *tbr[__REPLY_MAX], *tb[__TARGET_MAX];
-
-       blobmsg_buf_init(&boardbuf);
-
-       if ((rc = server_request(url, NULL, &boardbuf))) {
-               blob_buf_free(&boardbuf);
-               return rc;
-       }
-
-       blobmsg_parse(reply_policy, __REPLY_MAX, tbr, blob_data(boardbuf.head), blob_len(boardbuf.head));
-
-       if (!tbr[REPLY_OBJECT])
-               return -ENODATA;
-
-       blobmsg_parse(target_policy, __TARGET_MAX, tb, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
-
-       if (!tb[TARGET_METADATA_VERSION] ||
-           !tb[TARGET_ARCH_PACKAGES] ||
-           !tb[TARGET_IMAGES] ||
-           !tb[TARGET_TARGET]) {
-               blob_buf_free(&boardbuf);
-               return -ENODATA;
-       }
-
-       if (blobmsg_get_u32(tb[TARGET_METADATA_VERSION]) != 1) {
-               blob_buf_free(&boardbuf);
-               return -EPFNOSUPPORT;
-       }
-
-       if (strcmp(blobmsg_get_string(tb[TARGET_TARGET]), target))
-               return -EINVAL;
-
-       if (strcmp(blobmsg_get_string(tb[TARGET_ARCH_PACKAGES]), bver->branch->arch_packages))
-               return -EINVAL;
-
-       if (tb[TARGET_VERSION_CODE])
-               bver->version_code = strdup(blobmsg_get_string(tb[TARGET_VERSION_CODE]));
-
-       if (tb[TARGET_VERSION_NUMBER])
-               bver->version_number = strdup(blobmsg_get_string(tb[TARGET_VERSION_NUMBER]));
-
-       blob_buf_free(&boardbuf);
-       return 0;
-};
-
-static char* validate_target(struct blob_attr *branch)
-{
-       struct blob_attr *cur;
-       int rem;
-
-       blobmsg_for_each_attr(cur, branch, rem)
-               if (!strcmp(blobmsg_name(cur), target))
-                       return strdup(blobmsg_get_string(cur));
-
-       return NULL;
-}
-
-static void process_branch(struct blob_attr *branch, bool only_active)
-{
-       struct blob_attr *tb[__BRANCH_MAX], *pkc[__PACKAGE_CHANGES_MAX];
-       struct blob_attr *curver, *curpkc;
-       int remver, rempkc;
-       struct branch *br;
-       struct package_changes *pkce;
-       char *tmp, *board_json_file;
-       const char *brname;
-
-       blobmsg_parse(branches_policy, __BRANCH_MAX, tb, blobmsg_data(branch), blobmsg_len(branch));
-
-       /* mandatory fields */
-       if (!(tb[BRANCH_ENABLED] && blobmsg_get_bool(tb[BRANCH_ENABLED]) &&
-               tb[BRANCH_NAME] && tb[BRANCH_PATH] && tb[BRANCH_PATH_PACKAGES] &&
-               tb[BRANCH_VERSIONS] && tb[BRANCH_TARGETS]))
-               return;
-
-       brname = blobmsg_get_string(tb[BRANCH_NAME]);
-       if (only_active && strncmp(brname, version, strlen(brname)))
-               return;
-
-       br = calloc(1, sizeof(struct branch));
-       avl_init(&br->versions, avl_verrevcmp, false, NULL);
-       INIT_LIST_HEAD(&br->package_changes);
-
-       /* check if target is offered in branch and get arch_packages */
-       br->arch_packages = validate_target(tb[BRANCH_TARGETS]);
-       if (!br->arch_packages) {
-               free(br);
-               return;
-       }
-
-       if (tb[BRANCH_GIT_BRANCH])
-               br->git_branch = strdup(blobmsg_get_string(tb[BRANCH_GIT_BRANCH]));
-
-       if (tb[BRANCH_BRANCH_OFF_REV])
-               br->branch_off_rev = blobmsg_get_u32(tb[BRANCH_BRANCH_OFF_REV]);
-       else
-               br->branch_off_rev = 0;
-
-       if (tb[BRANCH_SNAPSHOT])
-               br->snapshot = blobmsg_get_bool(tb[BRANCH_SNAPSHOT]);
-       else
-               br->snapshot = false;
-
-       br->name = strdup(blobmsg_get_string(tb[BRANCH_NAME]));
-       br->path_packages = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES]), "branch", br->name);
-       if (!br->path_packages) {
-               free(br->name);
-               free(br->arch_packages);
-               free(br);
-               return;
-       }
-
-       /* parse package changes */
-       blobmsg_for_each_attr(curpkc, tb[BRANCH_PACKAGE_CHANGES], rempkc) {
-               if (blobmsg_type(curpkc) != BLOBMSG_TYPE_TABLE)
-                       continue;
-
-               blobmsg_parse(package_changes_policy, __PACKAGE_CHANGES_MAX, pkc, blobmsg_data(curpkc), blobmsg_len(curpkc));
-               if (!pkc[PACKAGE_CHANGES_REVISION] || (!pkc[PACKAGE_CHANGES_SOURCE] && !pkc[PACKAGE_CHANGES_TARGET]))
-                       continue;
-
-               pkce = calloc(1, sizeof(struct package_changes));
-               if (!pkce)
-                       break;
-
-               if (pkc[PACKAGE_CHANGES_SOURCE])
-                       pkce->source = strdup(blobmsg_get_string(pkc[PACKAGE_CHANGES_SOURCE]));
-
-               if (pkc[PACKAGE_CHANGES_TARGET])
-                       pkce->target = strdup(blobmsg_get_string(pkc[PACKAGE_CHANGES_TARGET]));
-
-               pkce->revision = blobmsg_get_u32(pkc[PACKAGE_CHANGES_REVISION]);
-
-               if (pkc[PACKAGE_CHANGES_MANDATORY])
-                       pkce->mandatory = blobmsg_get_bool(pkc[PACKAGE_CHANGES_MANDATORY]);
-
-               list_add_tail(&pkce->list, &br->package_changes);
-       }
-
-       /* add each version of the branch */
-       blobmsg_for_each_attr(curver, tb[BRANCH_VERSIONS], remver) {
-               if (blobmsg_type(curver) != BLOBMSG_TYPE_STRING)
-                       continue;
-
-               struct branch_version *bver = calloc(1, sizeof(struct branch_version));
-               bver->snapshot = !!strcasestr(blobmsg_get_string(curver), "snapshot");
-               bver->path = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH]), "version", blobmsg_get_string(curver));
-               if (!bver->path) {
-                       free(bver);
-                       continue;
-               }
-               bver->version = strdup(blobmsg_get_string(curver));
-               if (!bver->version) {
-                       free(bver->path);
-                       free(bver);
-                       continue;
-               }
-               bver->branch = br;
-               if (asprintf(&board_json_file, "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
-                            API_JSON_VERSION, bver->path, API_TARGETS, target, board_name,
-                            API_JSON_EXT) < 0) {
-                       free(bver->version);
-                       free(bver->path);
-                       free(bver);
-                       continue;
-               }
-               tmp = board_json_file;
-               while ((tmp = strchr(tmp, ',')))
-                       *tmp = '_';
-
-               if (request_target(bver, board_json_file)) {
-                       free(board_json_file);
-                       free(bver->version);
-                       free(bver->path);
-                       free(bver);
-                       continue;
-               }
-               free(board_json_file);
-               bver->avl.key = bver->version;
-               avl_insert(&br->versions, &bver->avl);
-       }
-
-       br->avl.key = br->name;
-       avl_insert(&branches, &br->avl);
-}
-
-static int request_branches(bool only_active)
-{
-       static struct blob_buf brbuf;
-       struct blob_attr *cur;
-       struct blob_attr *tb[__REPLY_MAX];
-       int rem;
-       char url[256];
-       struct blob_attr *data;
-
-       blobmsg_buf_init(&brbuf);
-       snprintf(url, sizeof(url), "%s/%s/%s/%s%s", serverurl, API_JSON,
-               API_JSON_VERSION, API_BRANCHES, API_JSON_EXT);
-
-       if ((rc = server_request(url, NULL, &brbuf))) {
-               blob_buf_free(&brbuf);
-               return rc;
-       };
-
-       blobmsg_parse(reply_policy, __REPLY_MAX, tb, blob_data(brbuf.head), blob_len(brbuf.head));
-
-       /* newer server API replies OBJECT, older API replies ARRAY... */
-       if ((!tb[REPLY_ARRAY] && !tb[REPLY_OBJECT]))
-               return -ENODATA;
-
-       if (tb[REPLY_OBJECT])
-               data = tb[REPLY_OBJECT];
-       else
-               data = tb[REPLY_ARRAY];
-
-       blobmsg_for_each_attr(cur, data, rem)
-               process_branch(cur, only_active);
-
-       blob_buf_free(&brbuf);
-
-       return 0;
-}
-
-static void free_branches()
-{
-       struct branch *br, *tmp;
-       struct branch_version *bver, *tmp2;
-       struct package_changes *pkce, *tmp3;
-
-       avl_for_each_element_safe(&branches, br, avl, tmp) {
-               free(br->name);
-               free(br->path_packages);
-               free(br->arch_packages);
-
-               avl_for_each_element_safe(&br->versions, bver, avl, tmp2) {
-                       avl_delete(&br->versions, &bver->avl);
-                       free(bver->version);
-                       free(bver->version_code);
-                       free(bver->version_number);
-                       free(bver->path);
-                       free(bver);
-               }
-
-               list_for_each_entry_safe(pkce, tmp3, &br->package_changes, list) {
-                       list_del(&pkce->list);
-                       free(pkce->source);
-                       free(pkce->target);
-                       free(pkce);
-               }
-
-               avl_delete(&branches, &br->avl);
-       }
-}
-
-static struct branch *get_current_branch()
-{
-       struct branch *br, *abr = NULL;
-
-       avl_for_each_element(&branches, br, avl) {
-               /* if branch name doesn't match version *prefix*, skip */
-               if (!strncasecmp(br->name, version, strlen(br->name))) {
-                       abr = br;
-                       break;
-               }
-       }
-
-       return abr;
-}
-
-static int revision_from_version_code(const char *version_code)
-{
-       int res;
-
-       if (sscanf(version_code, "r%d-", &res) == 1)
-               return res;
-
-       return -1;
-}
-
-static struct branch_version *select_branch(char *name, char *select_version)
-{
-       struct branch *br;
-       struct branch_version *bver, *abver = NULL;
-
-       if (!name)
-               name = version;
-
-       avl_for_each_element(&branches, br, avl) {
-               /* if branch name doesn't match version *prefix*, skip */
-               if (strncasecmp(br->name, name, strlen(br->name)))
-                       continue;
-
-               avl_for_each_element(&br->versions, bver, avl) {
-                       if (select_version) {
-                               if (!strcasecmp(bver->version, select_version)) {
-                                       abver = bver;
-                                       break;
-                               }
-                       } else {
-                               if (!strcasecmp(name, "snapshot")) {
-                                       /* we are on the main snapshot branch */
-                                       if (br->snapshot && bver->snapshot) {
-                                               abver = bver;
-                                               break;
-                                       }
-                               } else {
-                                       /* skip main snapshot branch */
-                                       if (br->snapshot)
-                                               continue;
-
-                                       if (strcasestr(version, "snapshot")) {
-                                               /* we are on a stable snapshot branch or coming from main snapshot branch */
-                                               if (bver->snapshot) {
-                                                       abver = bver;
-                                                       break;
-                                               }
-                                       } else {
-                                               if (bver->snapshot)
-                                                       continue;
-
-                                               if (!abver || (openwrt_release_verrevcmp(abver->version, bver->version) < 0))
-                                                       abver = bver;
-                                       }
-                               }
-                       }
-               }
-               if (abver)
-                       break;
-       }
-
-       return abver;
-}
-
-static int add_upg_packages(struct blob_attr *reply, char *arch)
-{
-       struct blob_attr *tbr[__REPLY_MAX];
-       struct blob_attr *tba[__PACKAGES_MAX];
-       struct blob_attr *packages;
-       struct blob_attr *cur;
-       int rem;
-       struct avl_pkg *avpk;
-
-       blobmsg_parse(reply_policy, __REPLY_MAX, tbr, blob_data(reply), blob_len(reply));
-
-       if (!tbr[REPLY_OBJECT])
-                       return -ENODATA;
-
-       if (arch) {
-               blobmsg_parse(packages_policy, __PACKAGES_MAX, tba, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
-               if (!tba[PACKAGES_ARCHITECTURE] ||
-                   !tba[PACKAGES_PACKAGES])
-                       return -ENODATA;
-
-               if (strcmp(blobmsg_get_string(tba[PACKAGES_ARCHITECTURE]), arch))
-                       return -EBADMSG;
-
-               packages = tba[PACKAGES_PACKAGES];
-       } else {
-               packages = tbr[REPLY_OBJECT];
-       }
-
-       blobmsg_for_each_attr(cur, packages, rem) {
-               avpk = calloc(1, sizeof(struct avl_pkg));
-               if (!avpk)
-                       return -ENOMEM;
-
-               avpk->name = strdup(blobmsg_name(cur));
-               if (!avpk->name) {
-                       free(avpk);
-                       return -ENOMEM;
-               }
-
-               avpk->version = strdup(blobmsg_get_string(cur));
-               if (!avpk->version) {
-                       free(avpk->name);
-                       free(avpk);
-                       return -ENOMEM;
-               }
-
-               avpk->avl.key = avpk->name;
-               if (avl_insert(&pkg_tree, &avpk->avl)) {
-
-#ifdef AUC_DEBUG
-                       if (debug)
-                               fprintf(stderr, "failed to insert package %s (%s)!\n", blobmsg_name(cur), blobmsg_get_string(cur));
-#endif
-
-                       if (avpk->name)
-                               free(avpk->name);
-
-                       if (avpk->version)
-                               free(avpk->version);
-
-                       free(avpk);
-               }
-       }
-
-       return 0;
-}
-
-static int request_packages(struct branch_version *bver)
-{
-       static struct blob_buf pkgbuf, archpkgbuf;
-       char url[256];
-       int ret;
-
-       fprintf(stderr, "Requesting package lists...\n");
-
-       blobmsg_buf_init(&archpkgbuf);
-       snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
-               API_JSON_VERSION, bver->path, API_TARGETS, target, API_INDEX, API_JSON_EXT);
-       if ((rc = server_request(url, NULL, &archpkgbuf))) {
-               blob_buf_free(&archpkgbuf);
-               return rc;
-       };
-
-       ret = add_upg_packages(archpkgbuf.head, bver->branch->arch_packages);
-       blob_buf_free(&archpkgbuf);
-
-       if (ret)
-               return ret;
-
-       blobmsg_buf_init(&pkgbuf);
-       snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s-%s%s", serverurl, API_JSON,
-               API_JSON_VERSION, bver->path, API_PACKAGES, bver->branch->arch_packages,
-               API_INDEX, API_JSON_EXT);
-       if ((rc = server_request(url, NULL, &pkgbuf))) {
-               blob_buf_free(&archpkgbuf);
-               blob_buf_free(&pkgbuf);
-               return rc;
-       };
-
-       ret = add_upg_packages(pkgbuf.head, NULL);
-       blob_buf_free(&pkgbuf);
-
-       return ret;
-}
-
-
-static int check_installed_packages(void)
-{
-       static struct blob_buf allpkg;
-       uint32_t id;
-       int status = 0;
-
-       blob_buf_init(&allpkg, 0);
-       blobmsg_add_u8(&allpkg, "all", 1);
-       blobmsg_add_string(&allpkg, "dummy", "foo");
-       if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
-           ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_check_cb, &status, 3000)) {
-               fprintf(stderr, "cannot request packagelist from rpcd\n");
-               status |= PKG_ERROR;
-       }
-
-       return status;
-}
-
-static int req_add_selected_packages(struct blob_buf *req)
-{
-       static struct blob_buf allpkg;
-       uint32_t id;
-
-       blob_buf_init(&allpkg, 0);
-       blobmsg_add_u8(&allpkg, "all", 0);
-       blobmsg_add_string(&allpkg, "dummy", "foo");
-       if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
-           ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_req_cb, req, 3000)) {
-               fprintf(stderr, "cannot request packagelist from rpcd\n");
-               return -EFAULT;
-       }
-
-       return 0;
-}
-
-#if defined(__amd64__) || defined(__i386__)
-static int system_is_efi(void)
-{
-       const char efidname[] = "/sys/firmware/efi/efivars";
-       int fd = open(efidname, O_DIRECTORY | O_PATH);
-
-       if (fd != -1) {
-               close(fd);
-               return 1;
-       } else {
-               return 0;
-       }
-}
-#else
-static inline int system_is_efi(void) { return 0; }
-#endif
-
-static int get_image_by_type(struct blob_attr *images, const char *typestr, const char *fstype, char **image_name, char **image_sha256)
-{
-       struct blob_attr *tb[__IMAGES_MAX];
-       struct blob_attr *cur;
-       int rem, ret = -ENOENT;
-
-       blobmsg_for_each_attr(cur, images, rem) {
-               blobmsg_parse(images_policy, __IMAGES_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
-               if (!tb[IMAGES_FILESYSTEM] ||
-                   !tb[IMAGES_NAME] ||
-                   !tb[IMAGES_TYPE] ||
-                   !tb[IMAGES_SHA256])
-                       continue;
-
-               if (fstype && strcmp(blobmsg_get_string(tb[IMAGES_FILESYSTEM]), fstype))
-                       continue;
-
-               if (!strcmp(blobmsg_get_string(tb[IMAGES_TYPE]), typestr)) {
-                       *image_name = strdup(blobmsg_get_string(tb[IMAGES_NAME]));
-                       *image_sha256 = strdup(blobmsg_get_string(tb[IMAGES_SHA256]));
-                       ret = 0;
-                       break;
-               }
-       }
-
-       return ret;
-}
-
-static int select_image(struct blob_attr *images, const char *target_fstype, char **image_name, char **image_sha256)
-{
-       const char *combined_type;
-       const char *fstype = rootfs_type;
-       int ret = -ENOENT;
-
-       if (target_fstype)
-               fstype = target_fstype;
-
-       if (system_is_efi())
-               combined_type = "combined-efi";
-       else
-               combined_type = "combined";
-
-       DPRINTF("images: %s\n", blobmsg_format_json_indent(images, true, 0));
-
-       if (fstype) {
-               ret = get_image_by_type(images, "sysupgrade", fstype, image_name, image_sha256);
-               if (!ret)
-                       return 0;
-
-               ret = get_image_by_type(images, combined_type, fstype, image_name, image_sha256);
-               if (!ret)
-                       return 0;
-
-               ret = get_image_by_type(images, "sdcard", fstype, image_name, image_sha256);
-               if (!ret)
-                       return 0;
-       }
-
-       /* fallback to squashfs unless fstype requested explicitly */
-       if (!target_fstype) {
-               ret = get_image_by_type(images, "sysupgrade", "squashfs", image_name, image_sha256);
-               if (!ret)
-                       return 0;
-
-               ret = get_image_by_type(images, combined_type, "squashfs", image_name, image_sha256);
-               if (!ret)
-                       return 0;
-
-               ret = get_image_by_type(images, "sdcard", fstype, image_name, image_sha256);
-       }
-
-       return ret;
-}
-
-static bool validate_sha256(char *filename, char *sha256str)
-{
-       char *cmd = calloc(strlen(SHA256SUM) + 1 + strlen(filename) + 1, sizeof(char));
-       size_t reslen = (64 + 2 + strlen(filename) + 1) * sizeof(char);
-       char *resstr = malloc(reslen);
-       FILE *f;
-       bool ret = false;
-
-       strcpy(cmd, SHA256SUM);
-       strcat(cmd, " ");
-       strcat(cmd, filename);
-
-       f = popen(cmd, "r");
-       if (!f)
-               goto sha256free;
-
-       if (fread(resstr, reslen, 1, f) < 1)
-               goto sha256close;
-
-       if (!strncmp(sha256str, resstr, 64))
-               ret = true;
-
-sha256close:
-       fflush(f);
-       pclose(f);
-sha256free:
-       free(cmd);
-       free(resstr);
-
-       return ret;
-}
-
-static inline bool status_delay(const char *status)
-{
-       return !strcmp(API_STATUS_QUEUED, status) ||
-              !strcmp(API_STATUS_STARTED, status);
-}
-
-static void usage(const char *arg0)
-{
-       fprintf(stdout, "%s: Attended sysUpgrade CLI client\n", arg0);
-       fprintf(stdout, "Usage: auc [-b <branch>] [-B <ver>] [-c] %s[-f] [-h] [-r] [-y]\n",
-#ifdef AUC_DEBUG
-"[-d] "
-#else
-""
-#endif
-               );
-       fprintf(stdout, " -b <branch>\tuse specific release branch\n");
-       fprintf(stdout, " -B <ver>\tuse specific release version\n");
-       fprintf(stdout, " -c\t\tonly check if system is up-to-date\n");
-#ifdef AUC_DEBUG
-       fprintf(stdout, " -d\t\tenable debugging output\n");
-#endif
-       fprintf(stdout, " -f\t\tuse force\n");
-       fprintf(stdout, " -h\t\toutput help\n");
-       fprintf(stdout, " -n\t\tdry-run (don't download or upgrade)\n");
-       fprintf(stdout, " -r\t\tcheck only for release upgrades\n");
-       fprintf(stdout, " -F <fstype>\toverride filesystem type\n");
-       fprintf(stdout, " -y\t\tdon't wait for user confirmation\n");
-       fprintf(stdout, "\n");
-       fprintf(stdout, "Please report issues to improve the server:\n");
-       fprintf(stdout, "%s\n", server_issues);
-}
-
-
-/* this main function is too big... todo: split */
-int main(int args, char *argv[]) {
-       static struct blob_buf checkbuf, infobuf, reqbuf, imgbuf, upgbuf;
-       struct branch *current_branch, *running_branch;
-       struct branch_version *target_version;
-       int running_revision, covered_revision = 0;
-       uint32_t id;
-       int valid;
-       char url[256];
-       char *sanetized_board_name, *image_name, *image_sha256, *tmp;
-       char *cmd_target_branch = NULL, *cmd_target_version = NULL, *cmd_target_fstype = NULL;
-       struct blob_attr *tbr[__REPLY_MAX];
-       struct blob_attr *tb[__TARGET_MAX] = {}; /* make sure tb is NULL initialized even if blobmsg_parse isn't called */
-       struct stat imgstat;
-       bool check_only = false;
-       bool retry_delay = false;
-       bool upg_check = false;
-       bool dry_run = false;
-       int revcmp = 0;
-       int addargs;
-       unsigned char argc = 1;
-       bool force = false, use_get = false, in_queue = false, release_only = false;
-
-       snprintf(user_agent, sizeof(user_agent), "%s/%s", argv[0], AUC_VERSION);
-       fprintf(stdout, "%s\n", user_agent);
-
-       while (argc<args) {
-               if (!strncmp(argv[argc], "-h", 3) ||
-                   !strncmp(argv[argc], "--help", 7)) {
-                       usage(argv[0]);
-                       return 0;
-               }
-
-               addargs = 0;
-#ifdef AUC_DEBUG
-               if (!strncmp(argv[argc], "-d", 3))
-                       debug = 1;
-#endif
-               if (!strncmp(argv[argc], "-b", 3)) {
-                       cmd_target_branch = argv[argc + 1];
-                       addargs = 1;
-               }
-
-               if (!strncmp(argv[argc], "-B", 3)) {
-                       cmd_target_version = argv[argc + 1];
-                       addargs = 1;
-               }
-
-               if (!strncmp(argv[argc], "-c", 3))
-                       check_only = true;
-
-               if (!strncmp(argv[argc], "-f", 3))
-                       force = true;
-
-               if (!strncmp(argv[argc], "-F", 3)) {
-                       cmd_target_fstype = argv[argc + 1];
-                       addargs = 1;
-               }
-
-               if (!strncmp(argv[argc], "-n", 3))
-                       dry_run = true;
-
-               if (!strncmp(argv[argc], "-r", 3))
-                       release_only = true;
-
-               if (!strncmp(argv[argc], "-y", 3))
-                       dont_ask = true;
-
-               argc += 1 + addargs;
-       };
-
-       if (load_config()) {
-               rc=-EFAULT;
-               goto freeubus;
-       }
-
-       if (chdir("/tmp")) {
-               rc=-EFAULT;
-               goto freeconfig;
-       }
-
-       if (!strncmp(serverurl, "https", 5)) {
-               rc = init_ustream_ssl();
-               if (rc == -2) {
-                       fprintf(stderr, "No CA certificates loaded, please install ca-certificates\n");
-                       rc=-1;
-                       goto freessl;
-               }
-
-               if (rc || !ssl_ctx) {
-                       fprintf(stderr, "SSL support not available, please install ustream-ssl\n");
-                       rc=-EPROTONOSUPPORT;
-                       goto freessl;
-               }
-       }
-
-       uloop_init();
-       ctx = ubus_connect(NULL);
-       if (!ctx) {
-               fprintf(stderr, "failed to connect to ubus.\n");
-               return -1;
-       }
-
-       blobmsg_buf_init(&checkbuf);
-       blobmsg_buf_init(&infobuf);
-       blobmsg_buf_init(&reqbuf);
-       blobmsg_buf_init(&imgbuf);
-       /* ubus requires BLOBMSG_TYPE_UNSPEC */
-       blob_buf_init(&upgbuf, 0);
-
-       if (ubus_lookup_id(ctx, "system", &id) ||
-           ubus_invoke(ctx, id, "board", NULL, board_cb, &checkbuf, 3000)) {
-               fprintf(stderr, "cannot request board info from procd\n");
-               rc=-EFAULT;
-               goto freebufs;
-       }
-
-       fprintf(stdout, "Server:    %s\n", serverurl);
-       fprintf(stdout, "Running:   %s %s on %s (%s)\n", version, revision, target, board_name);
-       if (cmd_target_fstype && rootfs_type && strcmp(rootfs_type, cmd_target_fstype))
-               fprintf(stderr, "WARNING: will change rootfs type from '%s' to '%s'\n",
-                       rootfs_type, cmd_target_fstype);
-
-       if (request_branches(!(cmd_target_branch || cmd_target_version))) {
-               rc=-ENODATA;
-               goto freeboard;
-       }
-
-       running_branch = get_current_branch();
-       running_revision = revision_from_version_code(revision);
-       if (!running_branch)
-               fprintf(stderr, "WARNING: cannot determing currently running branch.\n");
-
-       target_version = select_branch(cmd_target_branch, cmd_target_version);
-       if (!target_version) {
-               rc=-EINVAL;
-               goto freebranches;
-       }
-
-       fprintf(stdout, "Available: %s %s\n", target_version->version_number, target_version->version_code);
-
-       if (running_branch->snapshot && !target_version->branch->snapshot)
-               revcmp = (running_revision < target_version->branch->branch_off_rev)?-1:1;
-       else if (!running_branch->snapshot && target_version->branch->snapshot)
-               revcmp = -1;
-       else
-               revcmp = verrevcmp(version, target_version->version_number);
-
-       if (revcmp < 0)
-                       upg_check |= PKG_UPGRADE;
-       else if (revcmp > 0)
-                       upg_check |= PKG_DOWNGRADE;
-
-       if (release_only && !(upg_check & PKG_UPGRADE)) {
-               fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n");
-               rc = 0;
-               goto freebranches;
-       }
-
-       if (target_version->branch == running_branch)
-               grab_changes(running_branch, running_revision);
-       else if (revcmp > 0)
-               fprintf(stderr, "WARNING: Downgrade to older branch may not work as expected!\n");
-       else avl_for_element_range(running_branch, target_version->branch, current_branch, avl) {
-               if (current_branch == running_branch)
-                       grab_changes(running_branch, running_revision);
-               else
-                       grab_changes(current_branch, covered_revision);
-
-               if (current_branch->branch_off_rev > 0)
-                       covered_revision = current_branch->branch_off_rev;
-       }
-
-       if ((rc = request_packages(target_version)))
-               goto freebranches;
-
-       upg_check |= check_installed_packages();
-       if (upg_check & PKG_ERROR) {
-               rc = -ENOPKG;
-               goto freebranches;
-       }
-
-       if (!upg_check && !force) {
-               fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n");
-               rc=0;
-               goto freebranches;
-       };
-
-       if (!force && (upg_check & PKG_DOWNGRADE)) {
-               fprintf(stderr, "Refusing to downgrade. Use '-f' to force.\n");
-               rc = -ENOTRECOVERABLE;
-               goto freebranches;
-       };
-
-       if (!force && (upg_check & PKG_NOT_FOUND)) {
-               fprintf(stderr, "Not all installed packages found in remote lists. Use '-f' to force.\n");
-               rc = -ENOTRECOVERABLE;
-               goto freebranches;
-       };
-
-       if (check_only)
-               goto freebranches;
-
-       if (!dont_ask) {
-               if (!ask_user("Are you sure you want to continue the upgrade process?")) {
-                       rc = 0;
-                       goto freebranches;
-               }
-       }
-
-       blobmsg_add_string(&reqbuf, "version", target_version->version);
-       blobmsg_add_string(&reqbuf, "version_code", target_version->version_code);
-       blobmsg_add_string(&reqbuf, "target", target);
-
-       if (cmd_target_fstype || rootfs_type)
-               blobmsg_add_string(&reqbuf, "filesystem", cmd_target_fstype?cmd_target_fstype:rootfs_type);
-
-       sanetized_board_name = strdup(board_name);
-       tmp = sanetized_board_name;
-       while ((tmp = strchr(tmp, ',')))
-               *tmp = '_';
-
-       blobmsg_add_string(&reqbuf, "profile", sanetized_board_name);
-       blobmsg_add_u8(&reqbuf, "diff_packages", 1);
-
-       req_add_selected_packages(&reqbuf);
-
-       snprintf(url, sizeof(url), "%s/%s", serverurl, API_REQUEST);
-
-       use_get = false;
-       do {
-               retry = false;
-
-               DPRINTF("requesting from %s\n%s%s", url, use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0), use_get?"":"\n");
-
-               rc = server_request(url, use_get?NULL:&reqbuf, &imgbuf);
-               if (rc)
-                       break;
-
-               blobmsg_parse(reply_policy, __REPLY_MAX, tbr, blob_data(imgbuf.head), blob_len(imgbuf.head));
-               if (!tbr[REPLY_OBJECT])
-                       break;
-
-               blobmsg_parse(target_policy, __TARGET_MAX, tb, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
-
-               /* for compatibility with old server version, also support status in 200 reply */
-               if (tb[TARGET_STATUS]) {
-                       tmp = blobmsg_get_string(tb[TARGET_STATUS]);
-                       if (status_delay(tmp))
-                               retry = 1;
-               }
-
-               if (tb[TARGET_REQUEST_HASH]) {
-                       if (retry) {
-                               if (!retry_delay)
-                                       fputs("Requesting build", stderr);
-
-                               retry_delay = 2;
-                               if (tb[TARGET_QUEUE_POSITION]) {
-                                       fprintf(stderr, "%s%s (position in queue: %d)",
-                                               ANSI_ESC, in_queue?ANSI_CURSOR_RESTORE:ANSI_CURSOR_SAFE,
-                                               blobmsg_get_u32(tb[TARGET_QUEUE_POSITION]));
-                                       in_queue = true;
-                               } else {
-                                       if (in_queue)
-                                               fprintf(stderr, "%s%s%s%s",
-                                                       ANSI_ESC, ANSI_CURSOR_RESTORE,
-                                                       ANSI_ESC, ANSI_ERASE_LINE);
-                                       fputc('.', stderr);
-                                       in_queue = false;
-                               }
-                       } else {
-                               retry_delay = 0;
-                       }
-                       if (!use_get) {
-                               snprintf(url, sizeof(url), "%s/%s/%s", serverurl,
-                                        API_REQUEST,
-                                        blobmsg_get_string(tb[TARGET_REQUEST_HASH]));
-                               DPRINTF("polling via GET %s\n", url);
-                       }
-                       use_get = true;
-               } else if (retry_delay) {
-                       retry_delay = 0;
-               }
-
-#ifdef AUC_DEBUG
-               if (debug && tb[TARGET_STDOUT])
-                       fputs(blobmsg_get_string(tb[TARGET_STDOUT]), stdout);
-
-               if (debug && tb[TARGET_STDERR])
-                       fputs(blobmsg_get_string(tb[TARGET_STDERR]), stderr);
-#endif
-
-               if (retry) {
-                       blob_buf_free(&imgbuf);
-                       blobmsg_buf_init(&imgbuf);
-                       sleep(retry_delay);
-               }
-       } while(retry);
-
-       free(sanetized_board_name);
-
-       if (!tb[TARGET_IMAGES] || !tb[TARGET_BINDIR]) {
-               if (!rc)
-                       rc=-EBADMSG;
-               goto freebranches;
-       }
-
-       if ((rc = select_image(tb[TARGET_IMAGES], cmd_target_fstype, &image_name, &image_sha256)))
-               goto freebranches;
-
-       snprintf(url, sizeof(url), "%s/%s/%s/%s", serverurl, API_STORE,
-                blobmsg_get_string(tb[TARGET_BINDIR]),
-                image_name);
-
-       if (dry_run) {
-               fprintf(stderr, "\nImage available at %s\n", url);
-               rc = 0;
-               goto freebranches;
-       }
-
-       fprintf(stderr, "\nDownloading image from %s\n", url);
-       rc = server_request(url, NULL, NULL);
-       if (rc)
-               goto freebranches;
-
-       filename = uclient_get_url_filename(url, "firmware.bin");
-
-       if (stat(filename, &imgstat)) {
-               fprintf(stderr, "image download failed\n");
-               rc=-EPIPE;
-               goto freebranches;
-       }
-
-       if ((intmax_t)imgstat.st_size != out_len) {
-               fprintf(stderr, "file size mismatch\n");
-               unlink(filename);
-               rc=-EMSGSIZE;
-               goto freebranches;
-       }
-
-       if (!validate_sha256(filename, image_sha256)) {
-               fprintf(stderr, "sha256 mismatch\n");
-               unlink(filename);
-               rc=-EBADMSG;
-               goto freebranches;
-       }
-
-       if (strcmp(filename, "firmware.bin")) {
-               if (rename(filename, "firmware.bin")) {
-                       fprintf(stderr, "can't rename to firmware.bin\n");
-                       unlink(filename);
-                       rc=-errno;
-                       goto freebranches;
-               }
-       }
-
-       valid = 0;
-       if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
-           ubus_invoke(ctx, id, "upgrade_test", NULL, upgtest_cb, &valid, 15000)) {
-               rc=-EFAULT;
-               goto freebranches;
-       }
-
-       if (!valid) {
-               rc=-EINVAL;
-               goto freebranches;
-       }
-
-       fprintf(stdout, "invoking sysupgrade\n");
-       blobmsg_add_u8(&upgbuf, "keep", 1);
-       ubus_invoke(ctx, id, "upgrade_start", upgbuf.head, NULL, NULL, 120000);
-       sleep(10);
-
-freebranches:
-       free_branches();
-       if (rc && tb[TARGET_STDOUT]
-#ifdef AUC_DEBUG
-           && !debug
-#endif
-           )
-               fputs(blobmsg_get_string(tb[TARGET_STDOUT]), stdout);
-       if (rc && tb[TARGET_STDERR]
-#ifdef AUC_DEBUG
-           && !debug
-#endif
-           )
-               fputs(blobmsg_get_string(tb[TARGET_STDERR]), stderr);
-
-       if (tb[TARGET_DETAIL]) {
-               fputs(blobmsg_get_string(tb[TARGET_DETAIL]), stderr);
-               fputc('\n', stderr);
-       }
-
-freeboard:
-       if (rootfs_type)
-               free(rootfs_type);
-
-       free(board_name);
-       free(target);
-       free(distribution);
-       free(version);
-
-freebufs:
-       blob_buf_free(&checkbuf);
-       blob_buf_free(&infobuf);
-       blob_buf_free(&reqbuf);
-       blob_buf_free(&imgbuf);
-       blob_buf_free(&upgbuf);
-
-freessl:
-       if (ssl_ctx)
-               ssl_ops->context_free(ssl_ctx);
-
-freeconfig:
-       free(serverurl);
-
-freeubus:
-       uloop_done();
-       ubus_free(ctx);
-
-       if (ucl)
-               uclient_free(ucl);
-
-       if (dlh)
-               dlclose(dlh);
-
-       if (rc)
-               fprintf(stderr, "%s (%d)\n", strerror(-1 * rc), -1 * rc);
-
-       return rc;
-}