From 85ba80592ba67d4b47d914416ecec1f764dbc64b Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 9 Mar 2021 21:20:55 +0000 Subject: [PATCH] auc: update to new server API This updates auc to use the new API of the sysupgrade image server. Package version updates now need to be evaluated locally. Signed-off-by: Daniel Golle --- utils/auc/Makefile | 11 +- utils/auc/src/auc.c | 1049 +++++++++++++++++++++++++++++++++---------- 2 files changed, 804 insertions(+), 256 deletions(-) diff --git a/utils/auc/Makefile b/utils/auc/Makefile index 40cb6d185..df81f281d 100644 --- a/utils/auc/Makefile +++ b/utils/auc/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=auc -PKG_VERSION:=0.1.4 -PKG_RELEASE:=2 +PKG_VERSION:=0.1.5 +PKG_RELEASE:=$(AUTORELEASE) PKG_LICENSE:=GPL-3.0 include $(INCLUDE_DIR)/package.mk @@ -17,18 +17,15 @@ define Package/auc CATEGORY:=Base system TITLE:=Attended sysUpgrade CLI (EXPERIMENTAL) DEPENDS:=+attendedsysupgrade-common +libblobmsg-json +libubox +libubus \ - +libuci +libuclient +rpcd-mod-rpcsys +ucert + +libuci +libuclient +rpcd-mod-rpcsys endef 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) + $(if $(CONFIG_DEBUG),-DAUC_DEBUG=ON) define Package/auc/install $(INSTALL_DIR) $(1)/usr/sbin diff --git a/utils/auc/src/auc.c b/utils/auc/src/auc.c index 9bdd5298f..77cdb70ba 100644 --- a/utils/auc/src/auc.c +++ b/utils/auc/src/auc.c @@ -1,6 +1,6 @@ /* * auc - attendedsysUpgrade CLI - * Copyright (C) 2017 Daniel Golle + * Copyright (C) 2017-2021 Daniel Golle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 @@ -13,15 +13,18 @@ */ #define _GNU_SOURCE -#define AUC_VERSION "0.1.4" +#define AUC_VERSION "0.1.5" +#include #include #include #include #include +#include #include #include #include +#include #include #include @@ -36,8 +39,12 @@ #include #define REQ_TIMEOUT 15 -#define APIOBJ_CHECK "api/upgrade-check" -#define APIOBJ_REQUEST "api/upgrade-request" + +#define API_REQUEST "api/build" +#define API_JSON "json" +#define API_JSON_BRANCHES "json/branches.json" +#define API_STORE "store" +#define API_JSON_TARGETS "targets" #define PUBKEY_PATH "/etc/opkg/keys" @@ -47,8 +54,10 @@ #define DPRINTF(...) #endif -static const char server_issues[]="https://github.com/aparcar/attendedsysupgrade-server/issues"; +static const char server_issues[]="https://github.com/aparcar/asu/issues"; +static struct ubus_context *ctx; +static struct uclient *ucl = NULL; static char user_agent[80]; static char *serverurl; static int upgrade_packages; @@ -59,7 +68,7 @@ static off_t out_len; static off_t out_offset; static bool cur_resume; static int output_fd = -1; -static int retry, imagebuilder, building, ibready; +static bool retry = false; static char *board_name = NULL; static char *target = NULL; static char *distribution = NULL, *version = NULL, *revision = NULL; @@ -67,6 +76,34 @@ static int uptodate; static char *filename = NULL; static int rc; +struct branch { + struct list_head list; + char *name; + char *git_branch; + char *version; + bool snapshot; + time_t release_time; + time_t eol_time; + char *path; + char *path_packages; + char *arch_packages; + char *pubkey; + char *updates; + char **repos; + char **extra_repos; + char **extra_repos_names; + char **default_packages; + char **device_packages; +}; +static LIST_HEAD(branches); + +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 @@ -106,16 +143,18 @@ static const struct blobmsg_policy release_policy[__RELEASE_MAX] = { }; /* - * policy for packagelist - * see rpcd/sys.c + * policy for package list returned from rpc-sys or from server + * see rpcd/sys.c and ASU sources */ enum { - PACKAGELIST_PACKAGES, - __PACKAGELIST_MAX, + PACKAGES_ARCHITECTURE, + PACKAGES_PACKAGES, + __PACKAGES_MAX, }; -static const struct blobmsg_policy packagelist_policy[__PACKAGELIST_MAX] = { - [PACKAGELIST_PACKAGES] = { .name = "packages", .type = BLOBMSG_TYPE_TABLE }, +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 }, }; /* @@ -133,44 +172,124 @@ static const struct blobmsg_policy upgtest_policy[__UPGTEST_MAX] = { [UPGTEST_STDERR] = { .name = "stderr", .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 to extract version from upgrade-check response + * policy for branches.json */ enum { - CHECK_VERSION, - CHECK_UPGRADES, - __CHECK_MAX, + BRANCH_NAME, + BRANCH_ENABLED, + BRANCH_SNAPSHOT, + BRANCH_VERSIONS, + BRANCH_GIT_BRANCH, + BRANCH_DATE_RELEASE, + BRANCH_DATE_EOL, + BRANCH_PATH, + BRANCH_PATH_PACKAGES, + BRANCH_PUBKEY, + BRANCH_UPDATES, + BRANCH_REPOS, + BRANCH_EXTRA_REPOS, + BRANCH_TARGETS, + __BRANCH_MAX, }; -static const struct blobmsg_policy check_policy[__CHECK_MAX] = { - [CHECK_VERSION] = { .name = "version", .type = BLOBMSG_TYPE_STRING }, - [CHECK_UPGRADES] = { .name = "upgrades", .type = BLOBMSG_TYPE_TABLE }, +static const struct blobmsg_policy branches_policy[__BRANCH_MAX] = { + [BRANCH_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL }, + [BRANCH_SNAPSHOT] = { .name = "snapshot", .type = BLOBMSG_TYPE_BOOL }, + [BRANCH_VERSIONS] = { .name = "versions", .type = BLOBMSG_TYPE_ARRAY }, + [BRANCH_DATE_RELEASE] = { .name = "release_date", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_DATE_EOL] = { .name = "eol", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_GIT_BRANCH] = { .name = "git_branch", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_PATH_PACKAGES] = { .name = "path_packages", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_PUBKEY] = { .name = "pubkey", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_UPDATES] = { .name = "updates", .type = BLOBMSG_TYPE_STRING }, + [BRANCH_REPOS] = { .name = "repos", .type = BLOBMSG_TYPE_ARRAY }, + [BRANCH_EXTRA_REPOS] = { .name = "extra_repos", .type = BLOBMSG_TYPE_TABLE }, + [BRANCH_TARGETS] = { .name = "targets", .type = BLOBMSG_TYPE_TABLE }, +}; + +/* + * shared policy for target.json and server image request reply + */ +enum { + TARGET_METADATA_VERSION, + TARGET_ARCH_PACKAGES, + TARGET_DEFAULT_PACKAGES, + TARGET_DEVICE_PACKAGES, + TARGET_IMAGE_PREFIX, + TARGET_IMAGES, + TARGET_SOURCE_DATE_EPOCH, + TARGET_SUPPORTED_DEVICES, + TARGET_TARGET, + TARGET_TITLES, + TARGET_VERSION_CODE, + TARGET_VERSION_NUMBER, + TARGET_BUILD_AT, + TARGET_ENQUEUED_AT, + TARGET_MANIFEST, + TARGET_REQUEST_HASH, + TARGET_STATUS, + TARGET_STDERR, + TARGET_STDOUT, + TARGET_BINDIR, + __TARGET_MAX, }; -static const struct blobmsg_policy pkg_upgrades_policy[2] = { - { .type = BLOBMSG_TYPE_STRING }, - { .type = BLOBMSG_TYPE_STRING }, +static const struct blobmsg_policy target_policy[__TARGET_MAX] = { + [TARGET_METADATA_VERSION] = { .name = "metadata_version", .type = BLOBMSG_TYPE_INT32 }, + [TARGET_ARCH_PACKAGES] = { .name = "arch_packages", .type = BLOBMSG_TYPE_STRING }, + [TARGET_DEFAULT_PACKAGES] = { .name = "default_packages", .type = BLOBMSG_TYPE_ARRAY }, + [TARGET_DEVICE_PACKAGES] = { .name = "device_packages", .type = BLOBMSG_TYPE_ARRAY }, + [TARGET_IMAGE_PREFIX] = { .name = "image_prefix", .type = BLOBMSG_TYPE_STRING }, + [TARGET_IMAGES] = { .name = "images", .type = BLOBMSG_TYPE_ARRAY }, + [TARGET_SOURCE_DATE_EPOCH] = { .name = "source_date_epoch", .type = BLOBMSG_TYPE_STRING }, + [TARGET_SUPPORTED_DEVICES] = { .name = "supported_devices", .type = BLOBMSG_TYPE_ARRAY }, + [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 }, + [TARGET_BUILD_AT] = { .name = "built_at", .type = BLOBMSG_TYPE_STRING }, + [TARGET_ENQUEUED_AT] = { .name = "enqueued_at", .type = BLOBMSG_TYPE_STRING }, + [TARGET_MANIFEST] = { .name = "manifest", .type = BLOBMSG_TYPE_TABLE }, + [TARGET_REQUEST_HASH] = { .name = "request_hash", .type = BLOBMSG_TYPE_STRING }, + [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_BINDIR] = { .name = "bin_dir", .type = BLOBMSG_TYPE_STRING }, }; /* - * policy for upgrade-request response - * parse download information for the ready image. + * policy for images object in target */ enum { - IMAGE_REQHASH, - IMAGE_FILESIZE, - IMAGE_URL, - IMAGE_CHECKSUM, - IMAGE_FILES, - IMAGE_SYSUPGRADE, - __IMAGE_MAX, + IMAGES_FILESYSTEM, + IMAGES_NAME, + IMAGES_SHA256, + IMAGES_TYPE, + __IMAGES_MAX, }; -static const struct blobmsg_policy image_policy[__IMAGE_MAX] = { - [IMAGE_REQHASH] = { .name = "request_hash", .type = BLOBMSG_TYPE_STRING }, - [IMAGE_FILES] = { .name = "files", .type = BLOBMSG_TYPE_STRING }, - [IMAGE_SYSUPGRADE] = { .name = "sysupgrade", .type = BLOBMSG_TYPE_STRING }, +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 }, }; /* @@ -236,25 +355,70 @@ static int load_config() { /** * UBUS response callbacks */ - /* * rpc-sys packagelist - * append packagelist response to blobbuf given in req->priv + * append array of package names to blobbuf given in req->priv */ -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]; +#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" - blobmsg_parse(packagelist_policy, __PACKAGELIST_MAX, tb, blob_data(msg), blob_len(msg)); +#define PKG_UPGRADE 0x1 +#define PKG_DOWNGRADE 0x2 +#define PKG_NOT_FOUND 0x4 +#define PKG_ERROR 0x8 - if (!tb[PACKAGELIST_PACKAGES]) { - fprintf(stderr, "No packagelist received\n"); - rc=-1; +static inline bool is_builtin_pkg(const char *pkgname) +{ + return !strcmp(pkgname, "libc") || + !strcmp(pkgname, "librt") || + !strcmp(pkgname, "libpthread") || + !strcmp(pkgname, "kernel"); +} + +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; + + blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); + + if (!tb[PACKAGES_PACKAGES]) return; - } - blobmsg_add_field(buf, BLOBMSG_TYPE_TABLE, "installed", blobmsg_data(tb[PACKAGELIST_PACKAGES]), blobmsg_data_len(tb[PACKAGELIST_PACKAGES])); -}; + blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) { + if (is_builtin_pkg(blobmsg_name(cur))) + continue; + + pkg = avl_find_element(&pkg_tree, blobmsg_name(cur), pkg, avl); + if (!pkg) { + fprintf(stderr, "installed package %s cannot be found in remote list!\n", blobmsg_name(cur)); + *status |= PKG_NOT_FOUND; + continue; + } + + cmpres = strcmp(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:""); + } +} /* * rpc-sys packagelist @@ -262,26 +426,29 @@ static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_att */ 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 *tb[__PACKAGES_MAX]; struct blob_attr *cur; int rem; void *array; - blobmsg_parse(packagelist_policy, __PACKAGELIST_MAX, tb, blob_data(msg), blob_len(msg)); + blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blob_data(msg), blob_len(msg)); - if (!tb[PACKAGELIST_PACKAGES]) { + if (!tb[PACKAGES_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_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) { + if (is_builtin_pkg(blobmsg_name(cur))) + continue; + + 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 @@ -296,14 +463,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; + rc=-ENODATA; return; } board_name = strdup(blobmsg_get_string(tb[BOARD_BOARD_NAME])); if (!tb[BOARD_RELEASE]) { fprintf(stderr, "No release received\n"); - rc=-1; + rc=-ENODATA; return; } @@ -315,7 +482,7 @@ static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg) !rel[RELEASE_VERSION] || !rel[RELEASE_REVISION]) { fprintf(stderr, "No release information received\n"); - rc=-1; + rc=-ENODATA; return; } @@ -415,8 +582,6 @@ static void header_done_cb(struct uclient *cl) { struct blob_attr *tb[__H_MAX]; uint64_t resume_offset = 0, resume_end, resume_size; - char *ibstatus; - unsigned int queuepos = 0; if (uclient_http_redirect(cl)) { fprintf(stderr, "Redirected to %s on %s\n", cl->url->location, cl->url->host); @@ -431,29 +596,29 @@ static void header_done_cb(struct uclient *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(policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta)); switch (cl->status_code) { case 400: request_done(cl); - rc=-1; + rc=-ESRCH; break; case 409: fprintf(stderr, "Conflicting packages requested\n"); request_done(cl); - rc=-2; + rc=-EINVAL; break; case 412: fprintf(stderr, "%s target %s (%s) not found. Please report this at %s\n", distribution, target, board_name, server_issues); request_done(cl); - rc=-2; + rc=-ENOSYS; break; case 413: fprintf(stderr, "image too big.\n"); - rc=-1; + rc=-E2BIG; request_done(cl); break; case 416: @@ -463,17 +628,18 @@ static void header_done_cb(struct uclient *cl) case 422: fprintf(stderr, "unknown package '%s' requested.\n", blobmsg_get_string(tb[H_UNKNOWN_PACKAGE])); - rc=-1; + rc=-ENOPKG; request_done(cl); break; case 501: fprintf(stderr, "ImageBuilder didn't produce sysupgrade file.\n"); - rc=-2; + rc=-ENODATA; request_done(cl); break; case 204: fprintf(stdout, "system is up to date.\n"); uptodate=1; + rc=0; request_done(cl); break; case 206: @@ -495,39 +661,8 @@ static void header_done_cb(struct uclient *cl) break; } case 202: - if (!tb[H_IBSTATUS]) - break; - - ibstatus = blobmsg_get_string(tb[H_IBSTATUS]); - - if (!strncmp(ibstatus, "queue", 6)) { - if (!imagebuilder) { - fprintf(stderr, "server is dispatching build job\n"); - imagebuilder=1; - } else { - if (tb[H_IBQUEUEPOS]) { - queuepos = atoi(blobmsg_get_string(tb[H_IBQUEUEPOS])); - fprintf(stderr, "build is in queue position %u.\n", queuepos); - } - } - retry=1; - } else if (!strncmp(ibstatus, "building", 9)) { - if (!building) { - fprintf(stderr, "server is now building image...\n"); - building=1; - } - retry=1; - } else if (!strncmp(ibstatus, "initialize", 11)) { - if (!ibready) { - fprintf(stderr, "server is setting up ImageBuilder...\n"); - ibready=1; - } - retry=1; - } else { - fprintf(stderr, "unrecognized remote imagebuilder status '%s'\n", ibstatus); - rc=-2; - } - // fall through + retry = true; + break; case 200: if (cl->priv) break; @@ -543,7 +678,7 @@ static void header_done_cb(struct uclient *cl) break; default: - fprintf(stderr, "HTTP error %d\n", cl->status_code); + DPRINTF("HTTP error %d\n", cl->status_code); request_done(cl); break; } @@ -590,8 +725,7 @@ static void read_data_cb(struct uclient *cl) if (jsobj) { - if (json_object_get_type(jsobj) == json_type_object) - blobmsg_add_object(outbuf, jsobj); + blobmsg_add_json_element(outbuf, "reply", jsobj); json_object_put(jsobj); break; @@ -641,26 +775,29 @@ static const struct uclient_cb check_cb = { }; static int server_request(const char *url, struct blob_buf *inbuf, struct blob_buf *outbuf) { - struct uclient *ucl; struct jsonblobber *jsb = NULL; - int rc = -1; + int rc = -ENOENT; char *post_data; out_offset = 0; out_bytes = 0; out_len = 0; - uloop_init(); - - ucl = uclient_new(url, NULL, &check_cb); if (outbuf) { jsb = malloc(sizeof(struct jsonblobber)); jsb->outbuf = outbuf; jsb->tok = json_tokener_new(); }; - uclient_http_set_ssl_ctx(ucl, ssl_ops, ssl_ctx, 1); - ucl->timeout_msecs = REQ_TIMEOUT * 1000; + 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; @@ -681,8 +818,6 @@ static int server_request(const char *url, struct blob_buf *inbuf, struct blob_b return rc; uloop_run(); - uloop_done(); - uclient_free(ucl); return 0; } @@ -697,17 +832,17 @@ static int init_ustream_ssl(void) { dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL); if (!dlh) - return -1; + return -ENOENT; ssl_ops = dlsym(dlh, "ustream_ssl_ops"); if (!ssl_ops) - return -1; + return -ENOENT; ssl_ctx = ssl_ops->context_new(false); glob("/etc/ssl/certs/*.crt", 0, NULL, &gl); if (!gl.gl_pathc) - return -2; + return -ENOKEY; for (i = 0; i < gl.gl_pathc; i++) ssl_ops->context_add_ca_crt_file(ssl_ctx, gl.gl_pathv[i]); @@ -719,39 +854,445 @@ static int ask_user(void) { fprintf(stderr, "Are you sure you want to continue the upgrade process? [N/y] "); if (getchar() != 'y') - return -1; + return -EINTR; return 0; } -static void print_package_updates(struct blob_attr *upgrades) { +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 time_t parse_reldate(char *str) +{ + int m, d, y; + + if (!str || + strlen(str) != 10 || + sscanf(str, "%4d-%2d-%2d", &y, &m, &d) != 3) + return (time_t)0; + + struct tm time = { + .tm_mday = d, + .tm_mon = m - 1, + .tm_year = y - 1900, + }; + return timegm(&time); +} + +static int json_to_string_arrays(struct blob_attr *j, char ***vars, char ***names) +{ + int i = 0; struct blob_attr *cur; - struct blob_attr *tb[2]; int rem; - blobmsg_for_each_attr(cur, upgrades, rem) { - blobmsg_parse_array(pkg_upgrades_policy, ARRAY_SIZE(policy), tb, blobmsg_data(cur), blobmsg_data_len(cur)); - if (!tb[0] || !tb[1]) + if (j) { + i = blobmsg_check_array(j, BLOBMSG_TYPE_STRING); + if (i < 0) + return i; + } + + if (i > 0) { + *vars = calloc((i + 1), sizeof(char *)); + if (names) + *names = calloc((i + 1), sizeof(char *)); + + i = 0; + blobmsg_for_each_attr(cur, j, rem) { + if (names) + (*names)[i] = strdup(blobmsg_name(cur)); + + (*vars)[i++] = strdup(blobmsg_get_string(cur)); + } + } + + return i; +} + +static int request_target(struct branch *br, char *url) +{ + static struct blob_buf boardbuf; + struct blob_attr *tbr[__REPLY_MAX], *tb[__TARGET_MAX]; + + blobmsg_buf_init(&boardbuf); + + if (server_request(url, NULL, &boardbuf)) { + blob_buf_free(&boardbuf); + return -EREMOTE; + } + + 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_DEFAULT_PACKAGES] || + !tb[TARGET_DEVICE_PACKAGES] || + !tb[TARGET_IMAGE_PREFIX] || + !tb[TARGET_IMAGES] || + !tb[TARGET_SOURCE_DATE_EPOCH] || + !tb[TARGET_SUPPORTED_DEVICES] || + !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]), br->arch_packages)) + return -EINVAL; + + json_to_string_arrays(tb[TARGET_DEFAULT_PACKAGES], &br->default_packages, NULL); + json_to_string_arrays(tb[TARGET_DEVICE_PACKAGES], &br->device_packages, NULL); + +/* tb[TARGET_TITLES] + tb[TARGET_VERSION_CODE] + 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]; + struct blob_attr *curver; + int remver; + struct branch *br; + char *tmp, *arch_packages, *board_json_file; + + 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_UPDATES] && tb[BRANCH_PUBKEY] && tb[BRANCH_REPOS] && + tb[BRANCH_VERSIONS] && tb[BRANCH_TARGETS]) + return; + + if (only_active && strcmp(blobmsg_get_string(tb[BRANCH_NAME]), version)) + return; + + /* check if target is offered in branch and get arch_packages */ + arch_packages = validate_target(tb[BRANCH_TARGETS]); + if (!arch_packages) + return; + + /* add each version of the branch */ + blobmsg_for_each_attr(curver, tb[BRANCH_VERSIONS], remver) { + br = malloc(sizeof(struct branch)); + + br->snapshot = tb[BRANCH_SNAPSHOT] && blobmsg_get_bool(tb[BRANCH_SNAPSHOT]); + if (tb[BRANCH_GIT_BRANCH]) + br->git_branch = strdup(blobmsg_get_string(tb[BRANCH_GIT_BRANCH])); + + br->name = strdup(blobmsg_get_string(tb[BRANCH_NAME])); + br->path = strdup(blobmsg_get_string(tb[BRANCH_PATH])); + br->path_packages = strdup(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES])); + br->pubkey = strdup(blobmsg_get_string(tb[BRANCH_PUBKEY])); + br->updates = strdup(blobmsg_get_string(tb[BRANCH_UPDATES])); + br->release_time = parse_reldate(blobmsg_get_string(tb[BRANCH_DATE_RELEASE])); + br->eol_time = parse_reldate(blobmsg_get_string(tb[BRANCH_DATE_RELEASE])); + + json_to_string_arrays(tb[BRANCH_REPOS], &br->repos, NULL); + json_to_string_arrays(tb[BRANCH_EXTRA_REPOS], &br->extra_repos, &br->extra_repos_names); + + br->version = strdup(blobmsg_get_string(curver)); + br->path = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH]), "version", br->version); + br->path_packages = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES]), "branch", br->name); + br->arch_packages = arch_packages; + if (!br->path || !br->path_packages) { + free(br); + continue; + } + + asprintf(&board_json_file, "%s/%s/%s/%s/%s/%s.json", serverurl, API_JSON, br->path, API_JSON_TARGETS, target, board_name); + tmp = board_json_file; + while ((tmp = strchr(tmp, ','))) + *tmp = '_'; + + if (request_target(br, board_json_file)) { + free(board_json_file); + free(br); continue; + } + + free(board_json_file); + list_add_tail(&br->list, &branches); + } +} - fprintf(stdout, "\t%s (%s -> %s)\n", blobmsg_name(cur), - blobmsg_get_string(tb[1]), blobmsg_get_string(tb[0])); +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]; + + fprintf(stderr, "Requesting available branches from server %s...\n", serverurl); + blobmsg_buf_init(&brbuf); + snprintf(url, sizeof(url), "%s/%s", serverurl, API_JSON_BRANCHES); + + if (server_request(url, NULL, &brbuf)) { + blob_buf_free(&brbuf); + return -EREMOTE; }; + + blobmsg_parse(reply_policy, __REPLY_MAX, tb, blob_data(brbuf.head), blob_len(brbuf.head)); + + if (!tb[REPLY_ARRAY]) + return -ENODATA; + + blobmsg_for_each_attr(cur, tb[REPLY_ARRAY], rem) + process_branch(cur, only_active); + + blob_buf_free(&brbuf); + + return 0; +} + +static struct branch *select_branch(char *name, char *select_version) +{ + struct branch *br, *abr = NULL; + + if (!name) + name = version; + + list_for_each_entry(br, &branches, list) { + if (strcmp(br->name, name)) + continue; + + if (select_version) { + if (!strcmp(br->version, select_version)) { + abr = br; + break; + } + } else { + if (!abr || (strcmp(br->version, abr->version) > 0)) + abr = br; + } + } + + return abr; +} + +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 = malloc(sizeof(struct avl_pkg)); + avpk->name = strdup(blobmsg_name(cur)); + avpk->version = strdup(blobmsg_get_string(cur)); + avpk->avl.key = avpk->name; + if (!avpk->name || !avpk->version || avl_insert(&pkg_tree, &avpk->avl)) { + fprintf(stderr, "failed to insert package %s (%s)!\n", blobmsg_name(cur), blobmsg_get_string(cur)); + if (avpk->name) + free(avpk->name); + + if (avpk->version) + free(avpk->version); + + free(avpk); + return -ENOMEM; + } + } + + return 0; +} + +static int request_packages(struct branch *branch) +{ + 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/targets/%s/index.json", serverurl, API_JSON, branch->path, target); + if (server_request(url, NULL, &archpkgbuf)) { + blob_buf_free(&archpkgbuf); + return -EREMOTE; + }; + + ret = add_upg_packages(archpkgbuf.head, branch->arch_packages); + blob_buf_free(&archpkgbuf); + + if (ret) + return ret; + + blobmsg_buf_init(&pkgbuf); + snprintf(url, sizeof(url), "%s/%s/%s/packages/%s-index.json", serverurl, API_JSON, branch->path, branch->arch_packages); + if (server_request(url, NULL, &pkgbuf)) { + blob_buf_free(&archpkgbuf); + blob_buf_free(&pkgbuf); + return -EREMOTE; + }; + + ret = add_upg_packages(pkgbuf.head, NULL); + blob_buf_free(&pkgbuf); + + return ret; +} + + +static int check_installed_packages(struct blob_attr *pkgs) +{ + 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; +} + +static int select_image(struct blob_attr *images, 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 (!strcmp(blobmsg_get_string(tb[IMAGES_TYPE]), "sysupgrade")) { + *image_name = strdup(blobmsg_get_string(tb[IMAGES_NAME])); + *image_sha256 = strdup(blobmsg_get_string(tb[IMAGES_SHA256])); + ret = 0; + break; + } + } + + return ret; +} + +static inline bool status_delay(const char *status) +{ + return !strcmp("queued", status) || + !strcmp("started", status); } /* this main function is too big... todo: split */ int main(int args, char *argv[]) { - static struct blob_buf allpkg, checkbuf, infobuf, reqbuf, imgbuf, upgbuf; - struct ubus_context *ctx = ubus_connect(NULL); + static struct blob_buf checkbuf, infobuf, reqbuf, imgbuf, upgbuf; + struct branch *branch; uint32_t id; - int valid, use_get; + int valid; char url[256]; - char *newversion = NULL; - struct blob_attr *tb[__IMAGE_MAX]; - struct blob_attr *tbc[__CHECK_MAX]; + char *sanetized_board_name, *image_name, *image_sha256, *tmp; + struct blob_attr *tbr[__REPLY_MAX]; + struct blob_attr *tb[__TARGET_MAX]; struct stat imgstat; int check_only = 0; + int retry_delay = 0; + int upg_check; unsigned char argc = 1; + bool force = false, use_get = false; + uloop_init(); + ctx = ubus_connect(NULL); + if (!ctx) { + fprintf(stderr, "failed to connect to ubus.\n"); + return -1; + } snprintf(user_agent, sizeof(user_agent), "%s (%s)", argv[0], AUC_VERSION); fprintf(stdout, "%s\n", user_agent); @@ -761,7 +1302,7 @@ int main(int args, char *argv[]) { 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"); + fprintf(stdout, " -f\tuse force\n"); #ifdef AUC_DEBUG fprintf(stdout, " -d\tenable debugging output\n"); #endif @@ -776,6 +1317,9 @@ int main(int args, char *argv[]) { if (!strncmp(argv[argc], "-c", 3)) check_only = 1; + if (!strncmp(argv[argc], "-f", 3)) + force = true; + argc++; }; @@ -784,12 +1328,12 @@ int main(int args, char *argv[]) { return -1; } if (load_config()) { - rc=-1; + rc=-EFAULT; goto freeubus; } if (chdir("/tmp")) { - rc=-1; + rc=-EFAULT; goto freeconfig; } @@ -803,7 +1347,7 @@ int main(int args, char *argv[]) { if (rc || !ssl_ctx) { fprintf(stderr, "SSL support not available, please install ustream-ssl\n"); - rc=-1; + rc=-EPROTONOSUPPORT; goto freessl; } } @@ -813,197 +1357,200 @@ int main(int args, char *argv[]) { blobmsg_buf_init(&reqbuf); blobmsg_buf_init(&imgbuf); /* 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)) { fprintf(stderr, "cannot request board info from procd\n"); - rc=-1; - goto freebufs; - } - - if (rc) + rc=-EFAULT; 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", 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 on %s (%s)\n", distribution, version, target, board_name); - fprintf(stdout, "checking %s for release upgrade%s\n", serverurl, - upgrade_packages?" or updated packages":""); - - - snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_CHECK); - uptodate=0; - - do { - retry=0; - 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; - }; - - if (retry) - sleep(3); - } while(retry); - - DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(infobuf.head, true, 0)); - - blobmsg_parse(check_policy, __CHECK_MAX, tbc, blob_data(infobuf.head), blob_len(infobuf.head)); - - if (!tbc[CHECK_VERSION] && !tbc[CHECK_UPGRADES]) { - if (uptodate) { - rc=0; - } else if (!rc) { - fprintf(stderr, "server reply invalid.\n"); - rc=-2; - } + if (request_branches(true)) { + rc=-ENETUNREACH; goto freeboard; } - if (tbc[CHECK_VERSION]) { - newversion = blobmsg_get_string(tbc[CHECK_VERSION]); - fprintf(stdout, "new %s release %s found.\n", distribution, newversion); - } else { - newversion = version; - fprintf(stdout, "staying on %s release version %s\n", distribution, version); - }; + branch = select_branch(NULL, NULL); + if (!branch) { + rc=-EINVAL; + goto freebranches; + } - blobmsg_add_string(&reqbuf, "version", newversion); + if (request_packages(branch)) { + rc=-ECONNABORTED; + goto freebranches; + } - if (tbc[CHECK_UPGRADES]) { - fprintf(stdout, "package updates:\n"); - print_package_updates(tbc[CHECK_UPGRADES]); + upg_check = check_installed_packages(reqbuf.head); + if (upg_check & PKG_ERROR) { + rc=-ENOPKG; + goto freebranches; } - if (check_only) { - rc=1; - goto freeboard; + if (!upg_check && !force) { + rc=0; + goto freebranches; + }; + + if (!force && (upg_check & PKG_DOWNGRADE)) { + rc=-EBADSLT; + goto freebranches; }; + if (check_only) + goto freebranches; + rc = ask_user(); if (rc) - goto freeboard; + goto freebranches; - blobmsg_add_string(&reqbuf, "distro", distribution); + blobmsg_add_string(&reqbuf, "version", branch->version); blobmsg_add_string(&reqbuf, "target", target); - 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; - } + 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); - snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_REQUEST); + req_add_selected_packages(&reqbuf); - imagebuilder = 0; - building = 0; - use_get = 0; + snprintf(url, sizeof(url), "%s/%s", serverurl, API_REQUEST); + use_get = false; do { - retry = 0; + retry = false; - DPRINTF("requesting:\n%s\n", use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0)); + DPRINTF("requesting from %s\n%s%s", url, use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0), use_get?"":"\n"); server_request(url, use_get?NULL:&reqbuf, &imgbuf); - blobmsg_parse(image_policy, __IMAGE_MAX, tb, blob_data(imgbuf.head), blob_len(imgbuf.head)); - - if (!use_get && tb[IMAGE_REQHASH]) { - snprintf(url, sizeof(url), "%s/%s/%s", serverurl, - APIOBJ_REQUEST, - blobmsg_get_string(tb[IMAGE_REQHASH])); - DPRINTF("polling via GET %s\n", url); - retry=1; - use_get=1; + 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])); + + if (tb[TARGET_REQUEST_HASH] && tb[TARGET_STATUS]) { + if (status_delay(blobmsg_get_string(tb[TARGET_STATUS]))) { + if (!retry_delay) + fputs("Requesting build", stderr); + + retry_delay = 2; + fputc('.', stderr); + } 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); + } + retry = true; + use_get = true; + } else if (retry_delay) { + fputc('\n', stderr); + 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(3); + sleep(retry_delay); } } while(retry); - DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(imgbuf.head, true, 0)); - - if (!tb[IMAGE_SYSUPGRADE]) { - if (!rc) { - fprintf(stderr, "no sysupgrade image returned\n"); - rc=-1; - } - goto freeboard; + if (!tb[TARGET_IMAGES]) { + if (!rc) + rc=-ESTRPIPE; + goto freebranches; } - if (!tb[IMAGE_FILES]) { - if (!rc) { - fprintf(stderr, "no path to image files returned\n"); - rc=-1; - } - goto freeboard; + if (!tb[TARGET_BINDIR]) { + if (!rc) + rc=-ESTRPIPE; + goto freebranches; } - snprintf(url, sizeof(url), "%s/%s/%s", serverurl, - blobmsg_get_string(tb[IMAGE_FILES]), - blobmsg_get_string(tb[IMAGE_SYSUPGRADE])); + if ((rc = select_image(tb[TARGET_IMAGES], &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); + DPRINTF("downloading image from %s\n", url); 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; + rc=-EPIPE; + goto freebranches; } if ((intmax_t)imgstat.st_size != out_len) { fprintf(stderr, "file size mismatch\n"); unlink(filename); - rc=-1; - goto freeboard; + rc=-EMSGSIZE; + goto freebranches; } if (strcmp(filename, "firmware.bin")) { if (rename(filename, "firmware.bin")) { fprintf(stderr, "can't rename to firmware.bin\n"); unlink(filename); - rc=-1; - goto freeboard; + rc=-errno; + goto freebranches; } } valid = 0; - ubus_invoke(ctx, id, "upgrade_test", NULL, upgtest_cb, &valid, 15000); + 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=-1; - goto freeboard; + rc=-EINVAL; + goto freebranches; } fprintf(stderr, "invoking sysupgrade\n"); - blobmsg_add_u8(&upgbuf, "keep", 1); ubus_invoke(ctx, id, "upgrade_start", upgbuf.head, NULL, NULL, 120000); + sleep(10); +freebranches: + 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); + /* ToDo */ freeboard: free(board_name); free(target); @@ -1025,7 +1572,11 @@ freeconfig: free(serverurl); freeubus: + uloop_done(); ubus_free(ctx); + if (ucl) + uclient_free(ucl); + return rc; }