auc: update to version 0.0.9
authorDaniel Golle <redacted>
Thu, 22 Feb 2018 23:35:48 +0000 (00:35 +0100)
committerDaniel Golle <redacted>
Thu, 22 Feb 2018 23:51:18 +0000 (00:51 +0100)
 * use full package list when checking for upgrades
 * verify sha256sums and usign signature of sha256sums.sig
 * introduce '-c' option to only check if system is up-to-date
 * introduce '-F' option to ignore the signature verification result
 * return -1 on locally caused and -2 on server-side errors
 * don't include locally appended attributes in debug output of a
   server reply
 * output bug report note on 412 target not found
 * use content-length header instead of filesize field in JSON content
 * suppress duplicate error messages
 * drop unused attributes
 * make debug messages optional at compile-time

Signed-off-by: Daniel Golle <redacted>
utils/auc/Makefile
utils/auc/src/auc.c

index fa8290d3d905d30dda03f155428a2dbcfb3db156..1a62db1a799c8d9b6b8af7a3daf48a0be29d3785 100644 (file)
@@ -5,8 +5,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=auc
-PKG_VERSION:=0.0.8
-PKG_RELEASE=1
+PKG_VERSION:=0.0.9
+PKG_RELEASE:=1
 PKG_LICENSE:=GPL-3.0
 
 include $(INCLUDE_DIR)/package.mk
@@ -24,6 +24,12 @@ define Package/auc/description
    CLI client for attended-sysupgrade
 endef
 
+# set to 1 to enable debugging
+DEBUG:=1
+
+EXTRA_CFLAGS += \
+       $(if $(DEBUG),-DAUC_DEBUG=ON)
+
 define Package/auc/install
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/auc $(1)/usr/sbin/
index 21c13461de8663286e0fc116bb24703b8a64892e..4904e15e55494100b1012de5a43ffdb98b9b0912 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 #define _GNU_SOURCE
-#define AUC_VERSION "0.0.8"
+#define AUC_VERSION "0.0.9"
 
 #include <fcntl.h>
 #include <dlfcn.h>
 #define APIOBJ_CHECK "api/upgrade-check"
 #define APIOBJ_REQUEST "api/upgrade-request"
 
+#define PUBKEY_PATH "/etc/opkg/keys"
+
+#ifdef AUC_DEBUG
+#define DPRINTF(...) if (debug) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINTF(...)
+#endif
+
+static const char server_issues[]="https://github.com/aparcar/attendedsysupgrade-server/issues";
+
 static char user_agent[80];
 static char *serverurl;
 static int upgrade_packages;
@@ -52,30 +62,26 @@ static int output_fd = -1;
 static int retry, imagebuilder, building, ibready;
 static char *board_name = NULL;
 static char *target = NULL, *subtarget = NULL;
-static char *distribution = NULL, *version = NULL, *revision = NULL;
+static char *distribution = NULL, *version = NULL;
 static int uptodate;
 static char *filename = NULL;
+static int rc;
+
+#ifdef AUC_DEBUG
 static int debug = 0;
+#endif
 
 /*
  * policy for ubus call system board
  * see procd/system.c
  */
 enum {
-       BOARD_KERNEL,
-       BOARD_HOSTNAME,
-       BOARD_SYSTEM,
-       BOARD_MODEL,
        BOARD_BOARD_NAME,
        BOARD_RELEASE,
        __BOARD_MAX,
 };
 
 static const struct blobmsg_policy board_policy[__BOARD_MAX] = {
-       [BOARD_KERNEL] = { .name = "kernel", .type = BLOBMSG_TYPE_STRING },
-       [BOARD_HOSTNAME] = { .name = "hostname", .type = BLOBMSG_TYPE_STRING },
-       [BOARD_SYSTEM] = { .name = "system", .type = BLOBMSG_TYPE_STRING },
-       [BOARD_MODEL] = { .name = "model", .type = BLOBMSG_TYPE_STRING },
        [BOARD_BOARD_NAME] = { .name = "board_name", .type = BLOBMSG_TYPE_STRING },
        [BOARD_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_TABLE },
 };
@@ -87,20 +93,14 @@ static const struct blobmsg_policy board_policy[__BOARD_MAX] = {
 enum {
        RELEASE_DISTRIBUTION,
        RELEASE_VERSION,
-       RELEASE_REVISION,
-       RELEASE_CODENAME,
        RELEASE_TARGET,
-       RELEASE_DESCRIPTION,
        __RELEASE_MAX,
 };
 
 static const struct blobmsg_policy release_policy[__RELEASE_MAX] = {
        [RELEASE_DISTRIBUTION] = { .name = "distribution", .type = BLOBMSG_TYPE_STRING },
        [RELEASE_VERSION] = { .name = "version", .type = BLOBMSG_TYPE_STRING },
-       [RELEASE_REVISION] = { .name = "revision", .type = BLOBMSG_TYPE_STRING },
-       [RELEASE_CODENAME] = { .name = "codename", .type = BLOBMSG_TYPE_STRING },
        [RELEASE_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
-       [RELEASE_DESCRIPTION] = { .name = "description", .type = BLOBMSG_TYPE_STRING },
 };
 
 /*
@@ -146,6 +146,11 @@ static const struct blobmsg_policy check_policy[__CHECK_MAX] = {
        [CHECK_UPGRADES] = { .name = "upgrades", .type = BLOBMSG_TYPE_TABLE },
 };
 
+static const struct blobmsg_policy pkg_upgrades_policy[2] = {
+       { .type = BLOBMSG_TYPE_STRING },
+       { .type = BLOBMSG_TYPE_STRING },
+};
+
 /*
  * policy for upgrade-request response
  * parse download information for the ready image.
@@ -162,19 +167,37 @@ enum {
 
 static const struct blobmsg_policy image_policy[__IMAGE_MAX] = {
        [IMAGE_REQHASH] = { .name = "request_hash", .type = BLOBMSG_TYPE_STRING },
-       [IMAGE_FILESIZE] = { .name = "filesize", .type = BLOBMSG_TYPE_INT32 },
        [IMAGE_URL] = { .name = "url", .type = BLOBMSG_TYPE_STRING },
-       [IMAGE_CHECKSUM] = { .name = "checksum", .type = BLOBMSG_TYPE_STRING },
        [IMAGE_FILES] = { .name = "files", .type = BLOBMSG_TYPE_STRING },
        [IMAGE_SYSUPGRADE] = { .name = "sysupgrade", .type = BLOBMSG_TYPE_STRING },
 };
 
+/*
+ * policy for HTTP headers received from server
+ */
+enum {
+       H_RANGE,
+       H_LEN,
+       H_IBSTATUS,
+       H_IBQUEUEPOS,
+       H_UNKNOWN_PACKAGE,
+       __H_MAX
+};
+
+static const struct blobmsg_policy policy[__H_MAX] = {
+       [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
+       [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING },
+       [H_IBSTATUS] = { .name = "x-imagebuilder-status", .type = BLOBMSG_TYPE_STRING },
+       [H_IBQUEUEPOS] = { .name = "x-build-queue-position", .type = BLOBMSG_TYPE_STRING },
+       [H_UNKNOWN_PACKAGE] = { .name = "x-unknown-package", .type = BLOBMSG_TYPE_STRING },
+};
+
 /*
  * load serverurl from UCI
  */
 static int load_config() {
-       static struct uci_context *uci_ctx;
-       static struct uci_package *uci_attendedsysupgrade;
+       struct uci_context *uci_ctx;
+       struct uci_package *uci_attendedsysupgrade;
        struct uci_section *uci_s;
 
        uci_ctx = uci_alloc_context();
@@ -217,7 +240,7 @@ static int load_config() {
  * rpc-sys packagelist
  * append packagelist response to blobbuf given in req->priv
  */
-static void pkglist_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
+static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
        struct blob_buf *buf = (struct blob_buf *)req->priv;
        struct blob_attr *tb[__PACKAGELIST_MAX];
 
@@ -225,12 +248,39 @@ static void pkglist_cb(struct ubus_request *req, int type, struct blob_attr *msg
 
        if (!tb[PACKAGELIST_PACKAGES]) {
                fprintf(stderr, "No packagelist received\n");
+               rc=-1;
                return;
        }
 
        blobmsg_add_field(buf, BLOBMSG_TYPE_TABLE, "packages", blobmsg_data(tb[PACKAGELIST_PACKAGES]), blobmsg_data_len(tb[PACKAGELIST_PACKAGES]));
 };
 
+/*
+ * 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[__PACKAGELIST_MAX];
+       struct blob_attr *cur;
+       int rem;
+       void *array;
+
+       blobmsg_parse(packagelist_policy, __PACKAGELIST_MAX, tb, blob_data(msg), blob_len(msg));
+
+       if (!tb[PACKAGELIST_PACKAGES]) {
+               fprintf(stderr, "No packagelist received\n");
+               return;
+       }
+
+       array = blobmsg_open_array(buf, "packages");
+       blobmsg_for_each_attr(cur, tb[PACKAGELIST_PACKAGES], rem)
+               blobmsg_add_string(buf, NULL, blobmsg_name(cur));
+
+       blobmsg_close_array(buf, array);
+};
+
+
 /*
  * system board
  * append append board information to blobbuf given in req->priv
@@ -245,12 +295,14 @@ static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg)
 
        if (!tb[BOARD_BOARD_NAME]) {
                fprintf(stderr, "No board name received\n");
+               rc=-1;
                return;
        }
        board_name = strdup(blobmsg_get_string(tb[BOARD_BOARD_NAME]));
 
        if (!tb[BOARD_RELEASE]) {
                fprintf(stderr, "No release received\n");
+               rc=-1;
                return;
        }
 
@@ -259,6 +311,7 @@ static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg)
 
        if (!rel[RELEASE_TARGET]) {
                fprintf(stderr, "No target received\n");
+               rc=-1;
                return;
        }
 
@@ -268,7 +321,6 @@ static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg)
 
        distribution = strdup(blobmsg_get_string(rel[RELEASE_DISTRIBUTION]));
        version = strdup(blobmsg_get_string(rel[RELEASE_VERSION]));
-       revision = strdup(blobmsg_get_string(rel[RELEASE_REVISION]));
 
        blobmsg_add_string(buf, "distro", distribution);
        blobmsg_add_string(buf, "target", target);
@@ -354,19 +406,6 @@ static void request_done(struct uclient *cl)
 
 static void header_done_cb(struct uclient *cl)
 {
-       enum {
-               H_RANGE,
-               H_LEN,
-               H_IBSTATUS,
-               H_IBQUEUEPOS,
-               __H_MAX
-       };
-       static const struct blobmsg_policy policy[__H_MAX] = {
-               [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
-               [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING },
-               [H_IBSTATUS] = { .name = "x-imagebuilder-status", .type = BLOBMSG_TYPE_STRING },
-               [H_IBQUEUEPOS] = { .name = "x-build-queue-position", .type = BLOBMSG_TYPE_STRING },
-       };
        struct blob_attr *tb[__H_MAX];
        uint64_t resume_offset = 0, resume_end, resume_size;
        char *ibstatus;
@@ -385,21 +424,24 @@ static void header_done_cb(struct uclient *cl)
                return;
        }
 
-       if (debug)
-               fprintf(stderr, "headers:\n%s\n", blobmsg_format_json_indent(cl->meta, true, 0));
+       DPRINTF("headers:\n%s\n", blobmsg_format_json_indent(cl->meta, true, 0));
 
        blobmsg_parse(policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta));
 
        switch (cl->status_code) {
        case 400:
                request_done(cl);
+               rc=-1;
                break;
        case 412:
-               fprintf(stderr, "target not found.\n");
+               fprintf(stderr, "%s target %s/%s (%s) not found. Please report this at %s\n",
+                       distribution, target, subtarget, board_name, server_issues);
                request_done(cl);
+               rc=-2;
                break;
        case 413:
                fprintf(stderr, "image too big.\n");
+               rc=-1;
                request_done(cl);
                break;
        case 416:
@@ -407,16 +449,20 @@ static void header_done_cb(struct uclient *cl)
                request_done(cl);
                break;
        case 422:
-               fprintf(stderr, "unknown package requested.\n");
+               fprintf(stderr, "unknown package '%s' requested.\n",
+                       blobmsg_get_string(tb[H_UNKNOWN_PACKAGE]));
+               rc=-1;
                request_done(cl);
                break;
        case 501:
                fprintf(stderr, "ImageBuilder didn't produce sysupgrade file.\n");
+               rc=-2;
                request_done(cl);
                break;
        case 204:
-               fprintf(stderr, "system is up to date.\n");
+               fprintf(stdout, "system is up to date.\n");
                uptodate=1;
+               request_done(cl);
                break;
        case 206:
                if (!cur_resume) {
@@ -467,6 +513,7 @@ static void header_done_cb(struct uclient *cl)
                        retry=1;
                } else {
                        fprintf(stderr, "unrecognized remote imagebuilder status '%s'\n", ibstatus);
+                       rc=-2;
                }
                // fall through
        case 200:
@@ -657,45 +704,95 @@ static int init_ustream_ssl(void) {
 }
 
 /**
- * use busybox md5sum (from jow's luci-ng)
+ * use busybox sha256sum to verify sha256sums file
  */
-static char *md5sum(const char *file) {
+static int sha256sum_v(const char *sha256file, const char *msgfile) {
        pid_t pid;
        int fds[2];
-       static char md5[33];
+       int status;
+       FILE *f = fopen(sha256file, "r");
+       char sumline[512] = {};
+       char *fname;
+       unsigned int fnlen;
+       unsigned int cnt = 0;
 
        if (pipe(fds))
-               return NULL;
+               return -1;
+
+       if (!f)
+               return -1;
 
-       switch ((pid = fork()))
-       {
+
+       pid = fork();
+       switch (pid) {
        case -1:
-               return NULL;
+               return -1;
 
        case 0:
                uloop_done();
 
-               dup2(fds[1], 1);
-
-               close(0);
+               dup2(fds[0], 0);
+               close(1);
                close(2);
                close(fds[0]);
                close(fds[1]);
-
-               if (execl("/bin/busybox", "/bin/busybox", "md5sum", file, NULL));
-                       return NULL;
+               if (execl("/bin/busybox", "/bin/busybox", "sha256sum", "-s", "-c", NULL));
+                       return -1;
 
                break;
 
        default:
-               memset(md5, 0, sizeof(md5));
-               read(fds[0], md5, 32);
-               waitpid(pid, NULL, 0);
-               close(fds[0]);
+               while (fgets(sumline, sizeof(sumline), f)) {
+                       fname = &sumline[66];
+                       fnlen = strlen(fname);
+                       fname[fnlen-1] = '\0';
+                       if (!strcmp(fname, msgfile)) {
+                               fname[fnlen-1] = '\n';
+                               write(fds[1], sumline, strlen(sumline));
+                               cnt++;
+                       }
+               }
+               fclose(f);
                close(fds[1]);
+               waitpid(pid, &status, 0);
+               close(fds[0]);
+
+               if (cnt == 1)
+                       return WEXITSTATUS(status);
+               else
+                       return -1;
        }
 
-       return md5;
+       return -1;
+}
+
+/**
+ * use usign to verify sha256sums.sig
+ */
+static int usign_v(const char *file) {
+       pid_t pid;
+       int status;
+
+       pid = fork();
+       switch (pid) {
+       case -1:
+               return -1;
+
+       case 0:
+               uloop_done();
+
+               if (execl("/usr/bin/usign", "/usr/bin/usign",
+                         "-V", "-q", "-P", PUBKEY_PATH, "-m", file, NULL));
+                       return -1;
+
+               break;
+
+       default:
+               waitpid(pid, &status, 0);
+               return WEXITSTATUS(status);
+       }
+
+       return -1;
 }
 
 static int ask_user(void)
@@ -711,13 +808,8 @@ static void print_package_updates(struct blob_attr *upgrades) {
        struct blob_attr *tb[2];
        int rem;
 
-       static struct blobmsg_policy policy[2] = {
-               { .type = BLOBMSG_TYPE_STRING },
-               { .type = BLOBMSG_TYPE_STRING },
-       };
-
        blobmsg_for_each_attr(cur, upgrades, rem) {
-               blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
+               blobmsg_parse_array(pkg_upgrades_policy, ARRAY_SIZE(policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
                if (!tb[0] || !tb[1])
                        continue;
 
@@ -728,19 +820,19 @@ static void print_package_updates(struct blob_attr *upgrades) {
 
 /* this main function is too big... todo: split */
 int main(int args, char *argv[]) {
-       unsigned char argc=1;
-       static struct blob_buf checkbuf, reqbuf, imgbuf, upgbuf;
+       static struct blob_buf allpkg, checkbuf, infobuf, reqbuf, imgbuf, upgbuf;
        struct ubus_context *ctx = ubus_connect(NULL);
        uint32_t id;
-       int rc;
-       int queuepos, valid, use_get;
+       int valid, use_get;
        char url[256];
        char *newversion = NULL;
        struct blob_attr *tb[__IMAGE_MAX];
        struct blob_attr *tbc[__CHECK_MAX];
-       unsigned int filesize;
-       char *checksum = NULL;
+       char *tmp;
        struct stat imgstat;
+       int check_only = 0;
+       int ignore_sig = 0;
+       unsigned char argc = 1;
 
        snprintf(user_agent, sizeof(user_agent), "%s (%s)", argv[0], AUC_VERSION);
        fprintf(stdout, "%s\n", user_agent);
@@ -750,12 +842,25 @@ int main(int args, char *argv[]) {
                    !strncmp(argv[argc], "--help", 7)) {
                        fprintf(stdout, "%s: Attended sysUpgrade CLI client\n", argv[0]);
                        fprintf(stdout, "Usage: auc [-d] [-h]\n");
+                       fprintf(stdout, " -c\tonly check if system is up-to-date\n");
+                       fprintf(stdout, " -F\tignore result of signature verification\n");
+#ifdef AUC_DEBUG
                        fprintf(stdout, " -d\tenable debugging output\n");
+#endif
                        fprintf(stdout, " -h\toutput help\n");
                        return 0;
                }
+
+#ifdef AUC_DEBUG
                if (!strncmp(argv[argc], "-d", 3))
                        debug = 1;
+#endif
+               if (!strncmp(argv[argc], "-c", 3))
+                       check_only = 1;
+
+               if (!strncmp(argv[argc], "-F", 3))
+                       ignore_sig = 1;
+
                argc++;
        };
 
@@ -787,9 +892,12 @@ int main(int args, char *argv[]) {
        }
 
        blobmsg_buf_init(&checkbuf);
+       blobmsg_buf_init(&infobuf);
        blobmsg_buf_init(&reqbuf);
        blobmsg_buf_init(&imgbuf);
-       blobmsg_buf_init(&upgbuf);
+       /* ubus requires BLOBMSG_TYPE_UNSPEC */
+       blob_buf_init(&allpkg, 0);
+       blob_buf_init(&upgbuf, 0);
 
        if (ubus_lookup_id(ctx, "system", &id) ||
            ubus_invoke(ctx, id, "board", NULL, board_cb, &checkbuf, 3000)) {
@@ -798,34 +906,37 @@ int main(int args, char *argv[]) {
                goto freebufs;
        }
 
+       if (rc)
+               goto freebufs;
+
+       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", NULL, pkglist_cb, &checkbuf, 3000)) {
+           ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_check_cb, &checkbuf, 3000)) {
                fprintf(stderr, "cannot request packagelist from rpcd\n");
                rc=-1;
                goto freeboard;
        }
 
+       if (rc)
+               goto freeboard;
+
        blobmsg_add_u32(&checkbuf, "upgrade_packages", upgrade_packages);
 
-       fprintf(stdout, "running %s %s %s on %s/%s (%s)\n", distribution,
-               version, revision, target, subtarget, board_name);
+       fprintf(stdout, "running %s %s on %s/%s (%s)\n", distribution,
+               version, target, subtarget, board_name);
 
        fprintf(stdout, "checking %s for release upgrade%s\n", serverurl,
                upgrade_packages?" or updated packages":"");
 
-       blobmsg_add_string(&reqbuf, "distro", distribution);
-       blobmsg_add_string(&reqbuf, "target", target);
-       blobmsg_add_string(&reqbuf, "subtarget", subtarget);
-       blobmsg_add_string(&reqbuf, "board", board_name);
 
        snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_CHECK);
        uptodate=0;
 
        do {
                retry=0;
-               if (debug)
-                       fprintf(stderr, "requesting:\n%s\n", blobmsg_format_json_indent(checkbuf.head, true, 0));
-               if (server_request(url, &checkbuf, &reqbuf)) {
+               DPRINTF("requesting:\n%s\n", blobmsg_format_json_indent(checkbuf.head, true, 0));
+               if (server_request(url, &checkbuf, &infobuf)) {
                        fprintf(stderr, "failed to connect to server\n");
                        rc=-1;
                        goto freeboard;
@@ -835,20 +946,20 @@ int main(int args, char *argv[]) {
                        sleep(3);
        } while(retry);
 
-       if (debug)
-               fprintf(stderr, "reply:\n%s\n", blobmsg_format_json_indent(reqbuf.head, true, 0));
+       DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(infobuf.head, true, 0));
 
-       blobmsg_parse(check_policy, __CHECK_MAX, tbc, blob_data(reqbuf.head), blob_len(reqbuf.head));
+       blobmsg_parse(check_policy, __CHECK_MAX, tbc, blob_data(infobuf.head), blob_len(infobuf.head));
 
        if (!tbc[CHECK_VERSION] && !tbc[CHECK_UPGRADES]) {
-               if (!uptodate) {
+               if (uptodate) {
+                       rc=0;
+               } else if (!rc) {
                        fprintf(stderr, "server reply invalid.\n");
-                       rc=-1;
-                       goto freeboard;
+                       rc=-2;
                }
-               rc=0;
                goto freeboard;
        }
+
        if (tbc[CHECK_VERSION]) {
                newversion = blobmsg_get_string(tbc[CHECK_VERSION]);
                fprintf(stdout, "new %s release %s found.\n", distribution, newversion);
@@ -862,10 +973,29 @@ int main(int args, char *argv[]) {
                print_package_updates(tbc[CHECK_UPGRADES]);
        }
 
+       if (check_only) {
+               rc=1;
+               goto freeboard;
+       };
+
        rc = ask_user();
        if (rc)
                goto freeboard;
 
+       blobmsg_add_string(&reqbuf, "distro", distribution);
+       blobmsg_add_string(&reqbuf, "target", target);
+       blobmsg_add_string(&reqbuf, "subtarget", subtarget);
+       blobmsg_add_string(&reqbuf, "board", board_name);
+
+       blob_buf_init(&allpkg, 0);
+       blobmsg_add_u8(&allpkg, "all", 0);
+       blobmsg_add_string(&allpkg, "dummy", "foo");
+       if (ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_req_cb, &reqbuf, 3000)) {
+               fprintf(stderr, "cannot request packagelist from rpcd\n");
+               rc=-1;
+               goto freeboard;
+       }
+
        snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_REQUEST);
 
        imagebuilder = 0;
@@ -875,8 +1005,7 @@ int main(int args, char *argv[]) {
        do {
                retry = 0;
 
-               if (debug && !use_get)
-                       fprintf(stderr, "requesting:\n%s\n", blobmsg_format_json_indent(reqbuf.head, true, 0));
+               DPRINTF("requesting:\n%s\n", use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0));
 
                server_request(url, use_get?NULL:&reqbuf, &imgbuf);
                blobmsg_parse(image_policy, __IMAGE_MAX, tb, blob_data(imgbuf.head), blob_len(imgbuf.head));
@@ -885,9 +1014,7 @@ int main(int args, char *argv[]) {
                        snprintf(url, sizeof(url), "%s/%s/%s", serverurl,
                                 APIOBJ_REQUEST,
                                 blobmsg_get_string(tb[IMAGE_REQHASH]));
-                       if (debug)
-                               fprintf(stderr, "polling via GET %s\n", url);
-
+                       DPRINTF("polling via GET %s\n", url);
                        retry=1;
                        use_get=1;
                }
@@ -897,55 +1024,102 @@ int main(int args, char *argv[]) {
                        blobmsg_buf_init(&imgbuf);
                        sleep(3);
                }
-       } while(retry || queuepos);
+       } while(retry);
 
-       if (debug)
-               fprintf(stderr, "reply:\n%s\n", blobmsg_format_json_indent(imgbuf.head, true, 0));
+       DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(imgbuf.head, true, 0));
 
        if (!tb[IMAGE_SYSUPGRADE]) {
-               fprintf(stderr, "no sysupgrade image returned\n");
-               rc=-1;
+               if (!rc) {
+                       fprintf(stderr, "no sysupgrade image returned\n");
+                       rc=-1;
+               }
                goto freeboard;
        }
+
        strncpy(url, blobmsg_get_string(tb[IMAGE_SYSUPGRADE]), sizeof(url));
 
-       if (!tb[IMAGE_FILESIZE]) {
-               fprintf(stderr, "no image size returned\n");
+       server_request(url, NULL, NULL);
+
+       filename = uclient_get_url_filename(url, "firmware.bin");
+
+       if (stat(filename, &imgstat)) {
+               fprintf(stderr, "image download failed\n");
                rc=-1;
                goto freeboard;
        }
-       filesize = blobmsg_get_u32(tb[IMAGE_FILESIZE]);
 
-       if (!tb[IMAGE_CHECKSUM]) {
-               fprintf(stderr, "no image checksum returned\n");
+       if ((intmax_t)imgstat.st_size != out_len) {
+               fprintf(stderr, "file size mismatch\n");
+               unlink(filename);
                rc=-1;
                goto freeboard;
        }
-       checksum = blobmsg_get_string(tb[IMAGE_CHECKSUM]);
+
+       tmp=strrchr(url, '/');
+
+       strcpy(tmp, "/sha256sums");
        server_request(url, NULL, NULL);
-/* usign signature is not yet implemented! */
-//     strncat(url, ".sig", sizeof(url));
-//     server_request(url, NULL, NULL);
-       filename = uclient_get_url_filename(url, "firmware.bin");
 
-       if (stat(filename, &imgstat)) {
-               fprintf(stderr, "image download failed\n");
+       if (stat("sha256sums", &imgstat)) {
+               fprintf(stderr, "sha256sums download failed\n");
                rc=-1;
                goto freeboard;
        }
 
-       if ((intmax_t)imgstat.st_size != filesize) {
-               fprintf(stderr, "file size mismatch\n");
-               unlink(filename);
+       if ((intmax_t)imgstat.st_size != out_len) {
+               fprintf(stderr, "sha256sums download incomplete\n");
+               unlink("sha256sums");
                rc=-1;
                goto freeboard;
        }
 
-       if (strncmp(checksum, md5sum(filename), 33)) {
-               fprintf(stderr, "image checksum mismatch\n");
+       if (out_len < 68) {
+               fprintf(stderr, "sha256sums size mismatch\n");
+               unlink("sha256sums");
+               rc=-1;
+               goto freeboard;
+       }
+
+       if (sha256sum_v("sha256sums", filename)) {
+               fprintf(stderr, "checksum verification failed\n");
                unlink(filename);
+               unlink("sha256sums");
                rc=-1;
                goto freeboard;
+       }
+
+       strcpy(tmp, "/sha256sums.sig");
+       server_request(url, NULL, NULL);
+
+       if (stat("sha256sums.sig", &imgstat)) {
+               fprintf(stderr, "sha256sums.sig download failed\n");
+               rc=-1;
+               goto freeboard;
+       }
+
+       if ((intmax_t)imgstat.st_size != out_len) {
+               fprintf(stderr, "sha256sums.sig download incomplete\n");
+               unlink("sha256sums.sig");
+               rc=-1;
+               goto freeboard;
+       }
+
+       if (out_len < 16) {
+               fprintf(stderr, "sha256sums.sig size mismatch\n");
+               unlink("sha256sums.sig");
+               rc=-1;
+               goto freeboard;
+       }
+
+       if (usign_v("sha256sums")) {
+               fprintf(stderr, "signature verification failed\n");
+               if (!ignore_sig) {
+                       unlink(filename);
+                       unlink("sha256sums");
+                       unlink("sha256sums.sig");
+                       rc=-1;
+                       goto freeboard;
+               }
        };
 
        if (strcmp(filename, "firmware.bin")) {
@@ -974,10 +1148,10 @@ freeboard:
        /* subtarget is a pointer within target, don't free */
        free(distribution);
        free(version);
-       free(revision);
 
 freebufs:
        blob_buf_free(&checkbuf);
+       blob_buf_free(&infobuf);
        blob_buf_free(&reqbuf);
        blob_buf_free(&imgbuf);
        blob_buf_free(&upgbuf);
git clone https://git.99rst.org/PROJECT