/*
* auc - attendedsysUpgrade CLI
- * Copyright (C) 2017 Daniel Golle <daniel@makrotopia.org>
+ * 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
*/
#define _GNU_SOURCE
-#define AUC_VERSION "0.1.4"
+#ifndef AUC_VERSION
+#define AUC_VERSION "unknown"
+#endif
+#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 <libubus.h>
#define REQ_TIMEOUT 15
-#define APIOBJ_CHECK "api/upgrade-check"
-#define APIOBJ_REQUEST "api/upgrade-request"
+
+#define API_BRANCHES "branches"
+#define API_INDEX "index"
+#define API_JSON "json"
+#define API_JSON_EXT "." API_JSON
+#define API_PACKAGES "packages"
+#define API_REQUEST "api/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__)
#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;
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;
static char *filename = NULL;
static int rc;
+struct branch {
+ struct list_head list;
+ char *name;
+ char *git_branch;
+ char *version;
+ char *version_code;
+ char *version_number;
+ 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
*/
enum {
RELEASE_DISTRIBUTION,
- RELEASE_VERSION,
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_VERSION] = { .name = "version", .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 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 },
};
/*
[UPGTEST_STDERR] = { .name = "stderr", .type = BLOBMSG_TYPE_STRING },
};
+/*
+ * policy for branches.json
+ */
+enum {
+ BRANCH_DATE_EOL,
+ BRANCH_DATE_RELEASE,
+ BRANCH_ENABLED,
+ BRANCH_EXTRA_REPOS,
+ BRANCH_GIT_BRANCH,
+ BRANCH_NAME,
+ BRANCH_PATH,
+ BRANCH_PATH_PACKAGES,
+ BRANCH_PUBKEY,
+ BRANCH_REPOS,
+ BRANCH_SNAPSHOT,
+ BRANCH_TARGETS,
+ BRANCH_UPDATES,
+ BRANCH_VERSIONS,
+ __BRANCH_MAX,
+};
+
+static const struct blobmsg_policy branches_policy[__BRANCH_MAX] = {
+ [BRANCH_DATE_EOL] = { .name = "eol", .type = BLOBMSG_TYPE_STRING },
+ [BRANCH_DATE_RELEASE] = { .name = "release_date", .type = BLOBMSG_TYPE_STRING },
+ [BRANCH_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
+ [BRANCH_EXTRA_REPOS] = { .name = "extra_repos", .type = BLOBMSG_TYPE_TABLE },
+ [BRANCH_GIT_BRANCH] = { .name = "git_branch", .type = BLOBMSG_TYPE_STRING },
+ [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_PUBKEY] = { .name = "pubkey", .type = BLOBMSG_TYPE_STRING },
+ [BRANCH_REPOS] = { .name = "repos", .type = BLOBMSG_TYPE_ARRAY },
+ [BRANCH_SNAPSHOT] = { .name = "snapshot", .type = BLOBMSG_TYPE_BOOL },
+ [BRANCH_TARGETS] = { .name = "targets", .type = BLOBMSG_TYPE_TABLE },
+ [BRANCH_UPDATES] = { .name = "updates", .type = BLOBMSG_TYPE_STRING },
+ [BRANCH_VERSIONS] = { .name = "versions", .type = BLOBMSG_TYPE_ARRAY },
+};
/*
- * policy to extract version from upgrade-check response
+ * shared policy for target.json and server image request reply
*/
enum {
- CHECK_VERSION,
- CHECK_UPGRADES,
- __CHECK_MAX,
+ TARGET_ARCH_PACKAGES,
+ TARGET_BINDIR,
+ TARGET_BUILD_AT,
+ TARGET_DEFAULT_PACKAGES,
+ TARGET_DEVICE_PACKAGES,
+ TARGET_ENQUEUED_AT,
+ TARGET_IMAGE_PREFIX,
+ TARGET_IMAGES,
+ TARGET_MESSAGE,
+ TARGET_MANIFEST,
+ TARGET_METADATA_VERSION,
+ TARGET_REQUEST_HASH,
+ TARGET_SOURCE_DATE_EPOCH,
+ TARGET_STATUS,
+ TARGET_STDERR,
+ TARGET_STDOUT,
+ TARGET_SUPPORTED_DEVICES,
+ TARGET_TARGET,
+ TARGET_TITLES,
+ TARGET_VERSION_CODE,
+ TARGET_VERSION_NUMBER,
+ __TARGET_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 target_policy[__TARGET_MAX] = {
+ [TARGET_ARCH_PACKAGES] = { .name = "arch_packages", .type = BLOBMSG_TYPE_STRING },
+ [TARGET_BINDIR] = { .name = "bin_dir", .type = BLOBMSG_TYPE_STRING },
+ [TARGET_BUILD_AT] = { .name = "built_at", .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_ENQUEUED_AT] = { .name = "enqueued_at", .type = BLOBMSG_TYPE_STRING },
+ [TARGET_IMAGE_PREFIX] = { .name = "image_prefix", .type = BLOBMSG_TYPE_STRING },
+ [TARGET_IMAGES] = { .name = "images", .type = BLOBMSG_TYPE_ARRAY },
+ [TARGET_MANIFEST] = { .name = "manifest", .type = BLOBMSG_TYPE_TABLE },
+ [TARGET_MESSAGE] = { .name = "message", .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_SOURCE_DATE_EPOCH] = { .name = "source_date_epoch", .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_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 },
+};
+
+/*
+ * policy for images object in target
+ */
+enum {
+ IMAGES_FILESYSTEM,
+ IMAGES_NAME,
+ IMAGES_SHA256,
+ IMAGES_TYPE,
+ __IMAGES_MAX,
};
-static const struct blobmsg_policy pkg_upgrades_policy[2] = {
- { .type = BLOBMSG_TYPE_STRING },
- { .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 },
};
/*
- * policy for upgrade-request response
- * parse download information for the ready image.
+ * generic policy for HTTP JSON reply
*/
enum {
- IMAGE_REQHASH,
- IMAGE_FILESIZE,
- IMAGE_URL,
- IMAGE_CHECKSUM,
- IMAGE_FILES,
- IMAGE_SYSUPGRADE,
- __IMAGE_MAX,
+ REPLY_ARRAY,
+ REPLY_OBJECT,
+ __REPLY_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 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_RANGE,
H_LEN,
- H_IBSTATUS,
- H_IBQUEUEPOS,
+ H_RANGE,
H_UNKNOWN_PACKAGE,
__H_MAX
};
-static const struct blobmsg_policy policy[__H_MAX] = {
- [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
+static const struct blobmsg_policy header_policy[__H_MAX] = {
[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_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
[H_UNKNOWN_PACKAGE] = { .name = "x-unknown-package", .type = BLOBMSG_TYPE_STRING },
};
/**
* 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
*/
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;
+ struct avl_pkg *pkg;
+ void *table;
- 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));
+ table = blobmsg_open_table(buf, "packages_versions");
- blobmsg_close_array(buf, array);
-};
+ 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)
+ continue;
+ blobmsg_add_string(buf, blobmsg_name(cur), pkg->version);
+ }
+ blobmsg_close_table(buf, table);
+};
/*
* system board
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;
}
!rel[RELEASE_VERSION] ||
!rel[RELEASE_REVISION]) {
fprintf(stderr, "No release information received\n");
- rc=-1;
+ rc=-ENODATA;
return;
}
{
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);
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));
+ 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=-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:
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:
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;
request_done(cl);
}
break;
+ case 500:
+ /* server may reply JSON object */
+ break;
default:
- fprintf(stderr, "HTTP error %d\n", cl->status_code);
+ DPRINTF("HTTP error %d\n", cl->status_code);
request_done(cl);
break;
}
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;
};
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;
uclient_http_reset_headers(ucl);
uclient_http_set_header(ucl, "User-Agent", user_agent);
if (inbuf) {
- uclient_http_set_header(ucl, "Content-Type", "text/json");
+ 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));
}
return rc;
uloop_run();
- uloop_done();
- uclient_free(ucl);
return 0;
}
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]);
{
fprintf(stderr, "Are you sure you want to continue the upgrade process? [N/y] ");
if (getchar() != 'y')
- return -1;
+ return -EINTR;
+ 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 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;
+ int rem;
+
+ 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 ((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_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);
+
+ if (tb[TARGET_VERSION_CODE])
+ br->version_code = strdup(blobmsg_get_string(tb[TARGET_VERSION_CODE]));
+
+ if (tb[TARGET_VERSION_NUMBER])
+ br->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];
+ 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%s", serverurl, API_JSON,
+ br->path, API_TARGETS, target, board_name, API_JSON_EXT);
+ 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);
+ }
}
-static void print_package_updates(struct blob_attr *upgrades) {
+static int request_branches(bool only_active)
+{
+ static struct blob_buf brbuf;
struct blob_attr *cur;
- struct blob_attr *tb[2];
+ struct blob_attr *tb[__REPLY_MAX];
int rem;
+ char url[256];
+
+ blobmsg_buf_init(&brbuf);
+ snprintf(url, sizeof(url), "%s/%s/%s%s", serverurl, API_JSON,
+ 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));
- 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 (!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;
- fprintf(stdout, "\t%s (%s -> %s)\n", blobmsg_name(cur),
- blobmsg_get_string(tb[1]), blobmsg_get_string(tb[0]));
+ 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/%s/%s/%s%s", serverurl, API_JSON,
+ branch->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, 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", serverurl, API_JSON,
+ branch->path, API_PACKAGES, 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(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 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);
+ fclose(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);
}
/* 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 = 0;
+ int revcmp;
unsigned char argc = 1;
+ bool force = false, use_get = false;
snprintf(user_agent, sizeof(user_agent), "%s (%s)", argv[0], AUC_VERSION);
fprintf(stdout, "%s\n", user_agent);
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
if (!strncmp(argv[argc], "-c", 3))
check_only = 1;
+ if (!strncmp(argv[argc], "-f", 3))
+ force = true;
+
argc++;
};
- if (!ctx) {
- fprintf(stderr, "failed to connect to ubus.\n");
- return -1;
- }
if (load_config()) {
- rc=-1;
+ rc=-EFAULT;
goto freeubus;
}
if (chdir("/tmp")) {
- rc=-1;
+ rc=-EFAULT;
goto freeconfig;
}
if (rc || !ssl_ctx) {
fprintf(stderr, "SSL support not available, please install ustream-ssl\n");
- rc=-1;
+ 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(&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;
+ rc=-EFAULT;
goto freebufs;
}
- if (rc)
- goto freebufs;
+ fprintf(stdout, "Running: %s %s on %s (%s)\n", version, revision, target, board_name);
- 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;
+ if (request_branches(true)) {
+ rc=-ENETUNREACH;
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;
- };
+ branch = select_branch(NULL, NULL);
+ if (!branch) {
+ rc=-EINVAL;
+ goto freebranches;
+ }
- if (retry)
- sleep(3);
- } while(retry);
+ fprintf(stdout, "Available: %s %s\n", branch->version_number, branch->version_code);
- DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(infobuf.head, true, 0));
+ revcmp = strcmp(revision, branch->version_code);
+ if (revcmp < 0)
+ upg_check |= PKG_UPGRADE;
+ else if (revcmp > 0)
+ upg_check |= PKG_DOWNGRADE;
- blobmsg_parse(check_policy, __CHECK_MAX, tbc, blob_data(infobuf.head), blob_len(infobuf.head));
+ if ((rc = request_packages(branch)))
+ goto freebranches;
- if (!tbc[CHECK_VERSION] && !tbc[CHECK_UPGRADES]) {
- if (uptodate) {
- rc=0;
- } else if (!rc) {
- fprintf(stderr, "server reply invalid.\n");
- rc=-2;
- }
- goto freeboard;
+ upg_check |= check_installed_packages(reqbuf.head);
+ if (upg_check & PKG_ERROR) {
+ rc=-ENOPKG;
+ goto freebranches;
}
-
- 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);
+ if (!upg_check && !force) {
+ fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n");
+ rc=0;
+ goto freebranches;
};
- blobmsg_add_string(&reqbuf, "version", newversion);
-
- if (tbc[CHECK_UPGRADES]) {
- fprintf(stdout, "package updates:\n");
- print_package_updates(tbc[CHECK_UPGRADES]);
- }
-
- if (check_only) {
- rc=1;
- goto freeboard;
+ if (!force && (upg_check & PKG_DOWNGRADE)) {
+ fprintf(stderr, "Refusing to downgrade. Use '-f' to force.\n");
+ 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, "version_code", branch->version_code);
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 = '_';
- snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_REQUEST);
+ blobmsg_add_string(&reqbuf, "profile", sanetized_board_name);
+ blobmsg_add_u8(&reqbuf, "diff_packages", 1);
- imagebuilder = 0;
- building = 0;
- use_get = 0;
+ req_add_selected_packages(&reqbuf);
+ snprintf(url, sizeof(url), "%s/%s", serverurl, API_REQUEST);
+
+ use_get = false;
do {
- retry = 0;
+ 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;
- DPRINTF("requesting:\n%s\n", use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0));
+ blobmsg_parse(target_policy, __TARGET_MAX, tb, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
- server_request(url, use_get?NULL:&reqbuf, &imgbuf);
- blobmsg_parse(image_policy, __IMAGE_MAX, tb, blob_data(imgbuf.head), blob_len(imgbuf.head));
+ if (tb[TARGET_REQUEST_HASH] && tb[TARGET_STATUS]) {
+ if (status_delay(blobmsg_get_string(tb[TARGET_STATUS]))) {
+ if (!retry_delay)
+ fputs("Requesting build", stderr);
- 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;
+ 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));
+ free(sanetized_board_name);
- if (!tb[IMAGE_SYSUPGRADE]) {
- if (!rc) {
- fprintf(stderr, "no sysupgrade image returned\n");
- rc=-1;
- }
- goto freeboard;
+ if (!tb[TARGET_IMAGES] || !tb[TARGET_BINDIR]) {
+ if (!rc)
+ rc=-EBADMSG;
+ goto freebranches;
}
- if (!tb[IMAGE_FILES]) {
- if (!rc) {
- fprintf(stderr, "no path to image files returned\n");
- rc=-1;
- }
- goto freeboard;
- }
+ if ((rc = select_image(tb[TARGET_IMAGES], &image_name, &image_sha256)))
+ goto freebranches;
- snprintf(url, sizeof(url), "%s/%s/%s", serverurl,
- blobmsg_get_string(tb[IMAGE_FILES]),
- blobmsg_get_string(tb[IMAGE_SYSUPGRADE]));
+ snprintf(url, sizeof(url), "%s/%s/%s/%s", serverurl, API_STORE,
+ blobmsg_get_string(tb[TARGET_BINDIR]),
+ image_name);
- server_request(url, NULL, NULL);
+ DPRINTF("downloading 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=-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 (!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=-1;
- goto freeboard;
+ rc=-errno;
+ goto freebranches;
}
}
valid = 0;
- ubus_invoke(ctx, id, "upgrade_test", NULL, upgtest_cb, &valid, 15000);
- if (!valid) {
- rc=-1;
- goto freeboard;
+ if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
+ ubus_invoke(ctx, id, "upgrade_test", NULL, upgtest_cb, &valid, 15000)) {
+ rc=-EFAULT;
+ goto freebranches;
}
- fprintf(stderr, "invoking sysupgrade\n");
+ 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:
+ 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_MESSAGE]) {
+ fputs(blobmsg_get_string(tb[TARGET_MESSAGE]), stderr);
+ fputc('\n', stderr);
+ }
+
+ /* ToDo */
freeboard:
free(board_name);
free(target);
free(serverurl);
freeubus:
+ uloop_done();
ubus_free(ctx);
+ if (ucl)
+ uclient_free(ucl);
+
+ if (rc)
+ fprintf(stderr, "%s (%d)\n", strerror(-1 * rc), -1 * rc);
+
return rc;
}