auc: update to version 0.3.0

Most notably this brings support for processing package changes such
as suggesting to replace firewall with firewall4 when updating from
21.02.x -> 22.03.y release.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
(cherry picked from commit 36525086ce)
This commit is contained in:
Daniel Golle 2022-10-10 04:30:55 +01:00
parent 427008a2a6
commit 62a7ef57d4
No known key found for this signature in database
GPG key ID: 5A8F39C31C3217CA
2 changed files with 440 additions and 119 deletions

View file

@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=auc PKG_NAME:=auc
PKG_VERSION:=0.2.5 PKG_VERSION:=0.3.0
PKG_RELEASE:=$(AUTORELEASE) PKG_RELEASE:=$(AUTORELEASE)
PKG_LICENSE:=GPL-3.0 PKG_LICENSE:=GPL-3.0

View file

@ -64,7 +64,7 @@
#define DPRINTF(...) #define DPRINTF(...)
#endif #endif
static const char server_issues[]="https://github.com/aparcar/asu/issues"; static const char server_issues[]="https://github.com/openwrt/asu/issues";
static struct ubus_context *ctx; static struct ubus_context *ctx;
static struct uclient *ucl = NULL; static struct uclient *ucl = NULL;
@ -87,21 +87,40 @@ static int uptodate;
static char *filename = NULL; static char *filename = NULL;
static void *dlh = NULL; static void *dlh = NULL;
static int rc; static int rc;
static bool dont_ask = false;
struct branch { struct branch {
struct list_head list; struct avl_node avl;
char *name; char *name;
char *git_branch; char *git_branch;
char *path_packages;
char *arch_packages;
char **repos;
struct avl_tree versions;
struct list_head package_changes;
bool snapshot;
unsigned int branch_off_rev;
};
static struct avl_tree branches = AVL_TREE_INIT(branches, avl_strcmp, false, NULL);
struct branch_version {
struct avl_node avl;
struct branch *branch;
char *path;
char *version; char *version;
char *version_code; char *version_code;
char *version_number; char *version_number;
bool snapshot; bool snapshot;
char *path;
char *path_packages;
char *arch_packages;
char **repos;
}; };
static LIST_HEAD(branches);
struct package_changes {
struct list_head list;
unsigned int revision;
char *source;
char *target;
bool mandatory;
};
static LIST_HEAD(selected_package_changes);
struct avl_pkg { struct avl_pkg {
struct avl_node avl; struct avl_node avl;
@ -186,24 +205,47 @@ static const struct blobmsg_policy upgtest_policy[__UPGTEST_MAX] = {
enum { enum {
BRANCH_ENABLED, BRANCH_ENABLED,
BRANCH_GIT_BRANCH, BRANCH_GIT_BRANCH,
BRANCH_BRANCH_OFF_REV,
BRANCH_NAME, BRANCH_NAME,
BRANCH_PATH, BRANCH_PATH,
BRANCH_PATH_PACKAGES, BRANCH_PATH_PACKAGES,
BRANCH_SNAPSHOT, BRANCH_SNAPSHOT,
BRANCH_REPOS,
BRANCH_TARGETS, BRANCH_TARGETS,
BRANCH_UPDATES,
BRANCH_VERSIONS, BRANCH_VERSIONS,
BRANCH_PACKAGE_CHANGES,
__BRANCH_MAX, __BRANCH_MAX,
}; };
static const struct blobmsg_policy branches_policy[__BRANCH_MAX] = { static const struct blobmsg_policy branches_policy[__BRANCH_MAX] = {
[BRANCH_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL }, [BRANCH_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
[BRANCH_GIT_BRANCH] = { .name = "git_branch", .type = BLOBMSG_TYPE_STRING }, [BRANCH_GIT_BRANCH] = { .name = "git_branch", .type = BLOBMSG_TYPE_STRING },
[BRANCH_BRANCH_OFF_REV] = { .name = "branch_off_rev", .type = BLOBMSG_TYPE_INT32 },
[BRANCH_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, [BRANCH_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
[BRANCH_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [BRANCH_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
[BRANCH_PATH_PACKAGES] = { .name = "path_packages", .type = BLOBMSG_TYPE_STRING }, [BRANCH_PATH_PACKAGES] = { .name = "path_packages", .type = BLOBMSG_TYPE_STRING },
[BRANCH_SNAPSHOT] = { .name = "snapshot", .type = BLOBMSG_TYPE_BOOL }, [BRANCH_SNAPSHOT] = { .name = "snapshot", .type = BLOBMSG_TYPE_BOOL },
[BRANCH_REPOS] = { .name = "repos", .type = BLOBMSG_TYPE_ARRAY },
[BRANCH_TARGETS] = { .name = "targets", .type = BLOBMSG_TYPE_TABLE }, [BRANCH_TARGETS] = { .name = "targets", .type = BLOBMSG_TYPE_TABLE },
[BRANCH_UPDATES] = { .name = "updates", .type = BLOBMSG_TYPE_STRING },
[BRANCH_VERSIONS] = { .name = "versions", .type = BLOBMSG_TYPE_ARRAY }, [BRANCH_VERSIONS] = { .name = "versions", .type = BLOBMSG_TYPE_ARRAY },
[BRANCH_PACKAGE_CHANGES] = { .name = "package_changes", .type = BLOBMSG_TYPE_ARRAY },
};
enum {
PACKAGE_CHANGES_SOURCE,
PACKAGE_CHANGES_TARGET,
PACKAGE_CHANGES_REVISION,
PACKAGE_CHANGES_MANDATORY,
__PACKAGE_CHANGES_MAX,
};
static const struct blobmsg_policy package_changes_policy[__PACKAGE_CHANGES_MAX] = {
[PACKAGE_CHANGES_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_STRING },
[PACKAGE_CHANGES_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
[PACKAGE_CHANGES_REVISION] = { .name = "revision", .type = BLOBMSG_TYPE_INT32 },
[PACKAGE_CHANGES_MANDATORY] = { .name = "mandatory", .type = BLOBMSG_TYPE_BOOL },
}; };
/* /*
@ -402,6 +444,13 @@ static int verrevcmp(const char *val, const char *ref)
return 0; return 0;
} }
static int avl_verrevcmp(const void *k1, const void *k2, void *ptr)
{
const char *d1 = (const char *)k1, *d2 = (const char*)k2;
return verrevcmp(d1, d2);
}
/* /*
* replace '-rc' by '~' in string * replace '-rc' by '~' in string
*/ */
@ -436,6 +485,27 @@ static int openwrt_release_verrevcmp(const char *ver1, const char *ver2)
return verrevcmp(mver1, mver2); return verrevcmp(mver1, mver2);
} }
/*
* Select package_changes from branch to global list
*/
static void grab_changes(struct branch *br, unsigned int rev)
{
struct package_changes *c, *n;
#ifdef AUC_DEBUG
if (debug)
fprintf(stderr, "grabbing changes for branch %s from revision %u\n", br->name, rev);
#endif
list_for_each_entry(c, &br->package_changes, list) {
if (c->revision == 0 || c->revision > rev) {
n = malloc(sizeof(struct package_changes));
memcpy(n, c, sizeof(struct package_changes));
INIT_LIST_HEAD(&n->list);
list_add_tail(&n->list, &selected_package_changes);
}
}
}
/** /**
* UBUS response callbacks * UBUS response callbacks
@ -448,6 +518,7 @@ static int openwrt_release_verrevcmp(const char *ver1, const char *ver2)
#define ANSI_COLOR_RESET ANSI_ESC "[0m" #define ANSI_COLOR_RESET ANSI_ESC "[0m"
#define ANSI_COLOR_RED ANSI_ESC "[1;31m" #define ANSI_COLOR_RED ANSI_ESC "[1;31m"
#define ANSI_COLOR_GREEN ANSI_ESC "[1;32m" #define ANSI_COLOR_GREEN ANSI_ESC "[1;32m"
#define ANSI_COLOR_BLUE ANSI_ESC "[1;34m"
#define ANSI_CURSOR_SAFE "[s" #define ANSI_CURSOR_SAFE "[s"
#define ANSI_CURSOR_RESTORE "[u" #define ANSI_CURSOR_RESTORE "[u"
#define ANSI_ERASE_LINE "[K" #define ANSI_ERASE_LINE "[K"
@ -457,6 +528,19 @@ static int openwrt_release_verrevcmp(const char *ver1, const char *ver2)
#define PKG_NOT_FOUND 0x4 #define PKG_NOT_FOUND 0x4
#define PKG_ERROR 0x8 #define PKG_ERROR 0x8
static bool ask_user(const char *message)
{
char user_input;
fflush(stdin);
fprintf(stderr, "%s [N/y] ", message);
user_input = getchar();
fflush(stdin);
if ((user_input == 'y') || (user_input == 'Y'))
return true;
return false;
}
static inline bool is_builtin_pkg(const char *pkgname) static inline bool is_builtin_pkg(const char *pkgname)
{ {
return !strcmp(pkgname, "libc") || return !strcmp(pkgname, "libc") ||
@ -465,6 +549,42 @@ static inline bool is_builtin_pkg(const char *pkgname)
!strcmp(pkgname, "kernel"); !strcmp(pkgname, "kernel");
} }
static const char *apply_package_changes(const char *pkgname, bool interactive)
{
struct package_changes *pkc;
const char *mpkgname = pkgname;
list_for_each_entry(pkc, &selected_package_changes, list) {
/* package_change additions are dealt with later */
if (!pkc->source)
continue;
if (strcmp(pkc->source, mpkgname))
continue;
if (!pkc->mandatory && interactive) {
if (pkc->target)
fprintf(stderr, "Package %s should be replaced by %s.\n", pkc->source, pkc->target);
else
fprintf(stderr, "Package %s should be removed.\n", pkc->source);
if (dont_ask)
pkc->mandatory = true;
else
pkc->mandatory = ask_user("Apply change");
}
if (!pkc->mandatory)
continue;
mpkgname = pkc->target;
if (!mpkgname)
break;
}
return mpkgname;
}
static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_attr *msg) static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{ {
int *status = (int *)req->priv; int *status = (int *)req->priv;
@ -472,6 +592,8 @@ static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_att
struct avl_pkg *pkg; struct avl_pkg *pkg;
int rem; int rem;
int cmpres; int cmpres;
const char *pkgname;
struct package_changes *pkc;
blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
@ -479,17 +601,35 @@ static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_att
return; return;
blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) { blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) {
if (is_builtin_pkg(blobmsg_name(cur))) pkgname = blobmsg_name(cur);
if (is_builtin_pkg(pkgname))
continue; continue;
pkg = avl_find_element(&pkg_tree, blobmsg_name(cur), pkg, avl); pkgname = apply_package_changes(pkgname, true);
if (!pkgname) {
fprintf(stderr, " %s: %s%s -> (not installed)%s\n",
blobmsg_name(cur), ANSI_COLOR_BLUE,
blobmsg_get_string(cur), ANSI_COLOR_RESET);
continue;
}
pkg = avl_find_element(&pkg_tree, pkgname, pkg, avl);
if (!pkg) { if (!pkg) {
fprintf(stderr, "installed package %s%s%s cannot be found in remote list!\n", fprintf(stderr, "installed package %s%s%s cannot be found in remote list!\n",
ANSI_COLOR_RED, blobmsg_name(cur), ANSI_COLOR_RESET); ANSI_COLOR_RED, pkgname, ANSI_COLOR_RESET);
*status |= PKG_NOT_FOUND; *status |= PKG_NOT_FOUND;
continue; continue;
} }
if (pkgname != blobmsg_name(cur)) {
fprintf(stderr, " %s%s: %s -> %s: %s%s\n", ANSI_COLOR_BLUE,
blobmsg_name(cur),
blobmsg_get_string(cur), pkgname, pkg->version,
ANSI_COLOR_RESET);
*status |= PKG_UPGRADE;
continue;
}
cmpres = verrevcmp(blobmsg_get_string(cur), pkg->version); cmpres = verrevcmp(blobmsg_get_string(cur), pkg->version);
if (cmpres < 0) if (cmpres < 0)
*status |= PKG_UPGRADE; *status |= PKG_UPGRADE;
@ -507,6 +647,22 @@ static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_att
blobmsg_get_string(cur), pkg->version, blobmsg_get_string(cur), pkg->version,
(cmpres)?ANSI_COLOR_RESET:""); (cmpres)?ANSI_COLOR_RESET:"");
} }
list_for_each_entry(pkc, &selected_package_changes, list) {
/* deal only with package_change additions now */
if (pkc->source)
continue;
pkg = avl_find_element(&pkg_tree, pkc->target, pkg, avl);
if (!pkg) {
fprintf(stderr, "new package %s%s%s cannot be found in remote list!\n",
ANSI_COLOR_RED, pkc->target, ANSI_COLOR_RESET);
*status |= PKG_NOT_FOUND;
continue;
}
fprintf(stderr, " %s: %s(not installed) -> %s%s\n", pkc->target, ANSI_COLOR_BLUE,
pkg->version, ANSI_COLOR_RESET);
}
} }
/* /*
@ -520,6 +676,8 @@ static void pkglist_req_cb(struct ubus_request *req, int type, struct blob_attr
int rem; int rem;
struct avl_pkg *pkg; struct avl_pkg *pkg;
void *table; void *table;
const char *pkgname;
struct package_changes *pkc;
blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blob_data(msg), blob_len(msg)); blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blob_data(msg), blob_len(msg));
@ -531,14 +689,28 @@ static void pkglist_req_cb(struct ubus_request *req, int type, struct blob_attr
table = blobmsg_open_table(buf, "packages_versions"); table = blobmsg_open_table(buf, "packages_versions");
blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) { blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) {
if (is_builtin_pkg(blobmsg_name(cur))) pkgname = blobmsg_name(cur);
if (is_builtin_pkg(pkgname))
continue; continue;
pkg = avl_find_element(&pkg_tree, blobmsg_name(cur), pkg, avl); pkgname = apply_package_changes(pkgname, false);
pkg = avl_find_element(&pkg_tree, pkgname, pkg, avl);
if (!pkg) if (!pkg)
continue; continue;
blobmsg_add_string(buf, blobmsg_name(cur), pkg->version); blobmsg_add_string(buf, pkgname, pkg->version);
}
list_for_each_entry(pkc, &selected_package_changes, list) {
/* add new packages to request */
if (pkc->source)
continue;
pkg = avl_find_element(&pkg_tree, pkc->target, pkg, avl);
if (!pkg)
continue;
blobmsg_add_string(buf, pkc->target, pkg->version);
} }
blobmsg_close_table(buf, table); blobmsg_close_table(buf, table);
}; };
@ -929,17 +1101,6 @@ static int init_ustream_ssl(void) {
return 0; return 0;
} }
static int ask_user(void)
{
char user_input;
fprintf(stderr, "Are you sure you want to continue the upgrade process? [N/y] ");
user_input = getchar();
if ((user_input != 'y') && (user_input != 'Y'))
return -EINTR;
return 0;
}
static char* alloc_replace_var(char *in, const char *var, const char *replace) static char* alloc_replace_var(char *in, const char *var, const char *replace)
{ {
char *tmp = in; char *tmp = in;
@ -965,7 +1126,7 @@ static char* alloc_replace_var(char *in, const char *var, const char *replace)
return res; return res;
} }
static int request_target(struct branch *br, char *url) static int request_target(struct branch_version *bver, char *url)
{ {
static struct blob_buf boardbuf; static struct blob_buf boardbuf;
struct blob_attr *tbr[__REPLY_MAX], *tb[__TARGET_MAX]; struct blob_attr *tbr[__REPLY_MAX], *tb[__TARGET_MAX];
@ -1000,14 +1161,14 @@ static int request_target(struct branch *br, char *url)
if (strcmp(blobmsg_get_string(tb[TARGET_TARGET]), target)) if (strcmp(blobmsg_get_string(tb[TARGET_TARGET]), target))
return -EINVAL; return -EINVAL;
if (strcmp(blobmsg_get_string(tb[TARGET_ARCH_PACKAGES]), br->arch_packages)) if (strcmp(blobmsg_get_string(tb[TARGET_ARCH_PACKAGES]), bver->branch->arch_packages))
return -EINVAL; return -EINVAL;
if (tb[TARGET_VERSION_CODE]) if (tb[TARGET_VERSION_CODE])
br->version_code = strdup(blobmsg_get_string(tb[TARGET_VERSION_CODE])); bver->version_code = strdup(blobmsg_get_string(tb[TARGET_VERSION_CODE]));
if (tb[TARGET_VERSION_NUMBER]) if (tb[TARGET_VERSION_NUMBER])
br->version_number = strdup(blobmsg_get_string(tb[TARGET_VERSION_NUMBER])); bver->version_number = strdup(blobmsg_get_string(tb[TARGET_VERSION_NUMBER]));
blob_buf_free(&boardbuf); blob_buf_free(&boardbuf);
return 0; return 0;
@ -1027,11 +1188,12 @@ static char* validate_target(struct blob_attr *branch)
static void process_branch(struct blob_attr *branch, bool only_active) static void process_branch(struct blob_attr *branch, bool only_active)
{ {
struct blob_attr *tb[__BRANCH_MAX]; struct blob_attr *tb[__BRANCH_MAX], *pkc[__PACKAGE_CHANGES_MAX];
struct blob_attr *curver; struct blob_attr *curver, *curpkc;
int remver; int remver, rempkc;
struct branch *br; struct branch *br;
char *tmp, *arch_packages, *board_json_file; struct package_changes *pkce;
char *tmp, *board_json_file;
const char *brname; const char *brname;
blobmsg_parse(branches_policy, __BRANCH_MAX, tb, blobmsg_data(branch), blobmsg_len(branch)); blobmsg_parse(branches_policy, __BRANCH_MAX, tb, blobmsg_data(branch), blobmsg_len(branch));
@ -1046,47 +1208,111 @@ static void process_branch(struct blob_attr *branch, bool only_active)
if (only_active && strncmp(brname, version, strlen(brname))) if (only_active && strncmp(brname, version, strlen(brname)))
return; return;
br = calloc(1, sizeof(struct branch));
avl_init(&br->versions, avl_verrevcmp, false, NULL);
INIT_LIST_HEAD(&br->package_changes);
/* check if target is offered in branch and get arch_packages */ /* check if target is offered in branch and get arch_packages */
arch_packages = validate_target(tb[BRANCH_TARGETS]); br->arch_packages = validate_target(tb[BRANCH_TARGETS]);
if (!arch_packages) if (!br->arch_packages) {
free(br);
return; return;
}
if (tb[BRANCH_GIT_BRANCH])
br->git_branch = strdup(blobmsg_get_string(tb[BRANCH_GIT_BRANCH]));
if (tb[BRANCH_BRANCH_OFF_REV])
br->branch_off_rev = blobmsg_get_u32(tb[BRANCH_BRANCH_OFF_REV]);
else
br->branch_off_rev = 0;
if (tb[BRANCH_SNAPSHOT])
br->snapshot = blobmsg_get_bool(tb[BRANCH_SNAPSHOT]);
else
br->snapshot = false;
br->name = strdup(blobmsg_get_string(tb[BRANCH_NAME]));
br->path_packages = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES]), "branch", br->name);
if (!br->path_packages) {
free(br->name);
free(br->arch_packages);
free(br);
return;
}
/* parse package changes */
blobmsg_for_each_attr(curpkc, tb[BRANCH_PACKAGE_CHANGES], rempkc) {
if (blobmsg_type(curpkc) != BLOBMSG_TYPE_TABLE)
continue;
blobmsg_parse(package_changes_policy, __PACKAGE_CHANGES_MAX, pkc, blobmsg_data(curpkc), blobmsg_len(curpkc));
if (!pkc[PACKAGE_CHANGES_REVISION] || (!pkc[PACKAGE_CHANGES_SOURCE] && !pkc[PACKAGE_CHANGES_TARGET]))
continue;
pkce = calloc(1, sizeof(struct package_changes));
if (!pkce)
break;
if (pkc[PACKAGE_CHANGES_SOURCE])
pkce->source = strdup(blobmsg_get_string(pkc[PACKAGE_CHANGES_SOURCE]));
if (pkc[PACKAGE_CHANGES_TARGET])
pkce->target = strdup(blobmsg_get_string(pkc[PACKAGE_CHANGES_TARGET]));
pkce->revision = blobmsg_get_u32(pkc[PACKAGE_CHANGES_REVISION]);
if (pkc[PACKAGE_CHANGES_MANDATORY])
pkce->mandatory = blobmsg_get_bool(pkc[PACKAGE_CHANGES_MANDATORY]);
list_add_tail(&pkce->list, &br->package_changes);
}
/* add each version of the branch */ /* add each version of the branch */
blobmsg_for_each_attr(curver, tb[BRANCH_VERSIONS], remver) { blobmsg_for_each_attr(curver, tb[BRANCH_VERSIONS], remver) {
br = malloc(sizeof(struct branch)); if (blobmsg_type(curver) != BLOBMSG_TYPE_STRING)
continue;
if (tb[BRANCH_GIT_BRANCH]) struct branch_version *bver = calloc(1, sizeof(struct branch_version));
br->git_branch = strdup(blobmsg_get_string(tb[BRANCH_GIT_BRANCH])); bver->snapshot = !!strcasestr(blobmsg_get_string(curver), "snapshot");
bver->path = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH]), "version", blobmsg_get_string(curver));
br->name = strdup(blobmsg_get_string(tb[BRANCH_NAME])); if (!bver->path) {
br->path = strdup(blobmsg_get_string(tb[BRANCH_PATH])); free(bver);
br->path_packages = strdup(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES])); continue;
}
br->version = strdup(blobmsg_get_string(curver)); bver->version = strdup(blobmsg_get_string(curver));
br->snapshot = !!strcasestr(blobmsg_get_string(curver), "snapshot"); if (!bver->version) {
br->path = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH]), "version", br->version); free(bver->path);
br->path_packages = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES]), "branch", br->name); free(bver);
br->arch_packages = arch_packages; continue;
if (!br->path || !br->path_packages) { }
free(br); bver->branch = br;
if (asprintf(&board_json_file, "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
API_JSON_VERSION, bver->path, API_TARGETS, target, board_name,
API_JSON_EXT) < 0) {
free(bver->version);
free(bver->path);
free(bver);
continue; continue;
} }
asprintf(&board_json_file, "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
API_JSON_VERSION, br->path, API_TARGETS, target, board_name, API_JSON_EXT);
tmp = board_json_file; tmp = board_json_file;
while ((tmp = strchr(tmp, ','))) while ((tmp = strchr(tmp, ',')))
*tmp = '_'; *tmp = '_';
if (request_target(br, board_json_file)) { if (request_target(bver, board_json_file)) {
free(board_json_file); free(board_json_file);
free(br); free(bver->version);
free(bver->path);
free(bver);
continue; continue;
} }
free(board_json_file); free(board_json_file);
list_add_tail(&br->list, &branches); bver->avl.key = bver->version;
avl_insert(&br->versions, &bver->avl);
} }
br->avl.key = br->name;
avl_insert(&branches, &br->avl);
} }
static int request_branches(bool only_active) static int request_branches(bool only_active)
@ -1126,44 +1352,118 @@ static int request_branches(bool only_active)
return 0; return 0;
} }
static struct branch *select_branch(char *name, char *select_version) static void free_branches()
{
struct branch *br, *tmp;
struct branch_version *bver, *tmp2;
struct package_changes *pkce, *tmp3;
avl_for_each_element_safe(&branches, br, avl, tmp) {
free(br->name);
free(br->path_packages);
free(br->arch_packages);
avl_for_each_element_safe(&br->versions, bver, avl, tmp2) {
avl_delete(&br->versions, &bver->avl);
free(bver->version);
free(bver->version_code);
free(bver->version_number);
free(bver->path);
free(bver);
}
list_for_each_entry_safe(pkce, tmp3, &br->package_changes, list) {
list_del(&pkce->list);
free(pkce->source);
free(pkce->target);
free(pkce);
}
avl_delete(&branches, &br->avl);
}
}
static struct branch *get_current_branch()
{ {
struct branch *br, *abr = NULL; struct branch *br, *abr = NULL;
if (!name) avl_for_each_element(&branches, br, avl) {
name = version;
list_for_each_entry(br, &branches, list) {
/* if branch name doesn't match version *prefix*, skip */ /* if branch name doesn't match version *prefix*, skip */
if (strncasecmp(br->name, name, strlen(br->name))) if (!strncasecmp(br->name, version, strlen(br->name))) {
continue; abr = br;
break;
if (select_version) {
if (!strcasecmp(br->version, select_version)) {
abr = br;
break;
}
} else {
if (strcasestr(name, "snapshot")) {
/* if we are on the snapshot branch, stay there */
if (br->snapshot) {
abr = br;
break;
}
} else {
/* on release branch, skip snapshots and pick latest release */
if (br->snapshot)
continue;
if (!abr || (openwrt_release_verrevcmp(abr->version, br->version) < 0))
abr = br;
}
} }
} }
return abr; return abr;
} }
static int revision_from_version_code(const char *version_code)
{
int res;
if (sscanf(version_code, "r%d-", &res) == 1)
return res;
return -1;
}
static struct branch_version *select_branch(char *name, char *select_version)
{
struct branch *br;
struct branch_version *bver, *abver = NULL;
if (!name)
name = version;
avl_for_each_element(&branches, br, avl) {
/* if branch name doesn't match version *prefix*, skip */
if (strncasecmp(br->name, name, strlen(br->name)))
continue;
avl_for_each_element(&br->versions, bver, avl) {
if (select_version) {
if (!strcasecmp(bver->version, select_version)) {
abver = bver;
break;
}
} else {
if (!strcasecmp(name, "snapshot")) {
/* we are on the main snapshot branch */
if (br->snapshot && bver->snapshot) {
abver = bver;
break;
}
} else {
/* skip main snapshot branch */
if (br->snapshot)
continue;
if (strcasestr(version, "snapshot")) {
/* we are on a stable snapshot branch or coming from main snapshot branch */
if (bver->snapshot) {
abver = bver;
break;
}
} else {
if (bver->snapshot)
continue;
if (!abver || (openwrt_release_verrevcmp(abver->version, bver->version) < 0)) {
abver = bver;
break;
}
}
}
}
}
if (abver)
break;
}
return abver;
}
static int add_upg_packages(struct blob_attr *reply, char *arch) static int add_upg_packages(struct blob_attr *reply, char *arch)
{ {
struct blob_attr *tbr[__REPLY_MAX]; struct blob_attr *tbr[__REPLY_MAX];
@ -1193,7 +1493,7 @@ static int add_upg_packages(struct blob_attr *reply, char *arch)
} }
blobmsg_for_each_attr(cur, packages, rem) { blobmsg_for_each_attr(cur, packages, rem) {
avpk = malloc(sizeof(struct avl_pkg)); avpk = calloc(1, sizeof(struct avl_pkg));
if (!avpk) if (!avpk)
return -ENOMEM; return -ENOMEM;
@ -1231,7 +1531,7 @@ static int add_upg_packages(struct blob_attr *reply, char *arch)
return 0; return 0;
} }
static int request_packages(struct branch *branch) static int request_packages(struct branch_version *bver)
{ {
static struct blob_buf pkgbuf, archpkgbuf; static struct blob_buf pkgbuf, archpkgbuf;
char url[256]; char url[256];
@ -1241,13 +1541,13 @@ static int request_packages(struct branch *branch)
blobmsg_buf_init(&archpkgbuf); blobmsg_buf_init(&archpkgbuf);
snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON, snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
API_JSON_VERSION, branch->path, API_TARGETS, target, API_INDEX, API_JSON_EXT); API_JSON_VERSION, bver->path, API_TARGETS, target, API_INDEX, API_JSON_EXT);
if ((rc = server_request(url, NULL, &archpkgbuf))) { if ((rc = server_request(url, NULL, &archpkgbuf))) {
blob_buf_free(&archpkgbuf); blob_buf_free(&archpkgbuf);
return rc; return rc;
}; };
ret = add_upg_packages(archpkgbuf.head, branch->arch_packages); ret = add_upg_packages(archpkgbuf.head, bver->branch->arch_packages);
blob_buf_free(&archpkgbuf); blob_buf_free(&archpkgbuf);
if (ret) if (ret)
@ -1255,7 +1555,7 @@ static int request_packages(struct branch *branch)
blobmsg_buf_init(&pkgbuf); blobmsg_buf_init(&pkgbuf);
snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s-%s%s", serverurl, API_JSON, snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s-%s%s", serverurl, API_JSON,
API_JSON_VERSION, branch->path, API_PACKAGES, branch->arch_packages, API_JSON_VERSION, bver->path, API_PACKAGES, bver->branch->arch_packages,
API_INDEX, API_JSON_EXT); API_INDEX, API_JSON_EXT);
if ((rc = server_request(url, NULL, &pkgbuf))) { if ((rc = server_request(url, NULL, &pkgbuf))) {
blob_buf_free(&archpkgbuf); blob_buf_free(&archpkgbuf);
@ -1270,7 +1570,7 @@ static int request_packages(struct branch *branch)
} }
static int check_installed_packages(struct blob_attr *pkgs) static int check_installed_packages(void)
{ {
static struct blob_buf allpkg; static struct blob_buf allpkg;
uint32_t id; uint32_t id;
@ -1465,12 +1765,14 @@ static void usage(const char *arg0)
/* this main function is too big... todo: split */ /* this main function is too big... todo: split */
int main(int args, char *argv[]) { int main(int args, char *argv[]) {
static struct blob_buf checkbuf, infobuf, reqbuf, imgbuf, upgbuf; static struct blob_buf checkbuf, infobuf, reqbuf, imgbuf, upgbuf;
struct branch *branch; struct branch *current_branch, *running_branch;
struct branch_version *target_version;
int running_revision, covered_revision = 0;
uint32_t id; uint32_t id;
int valid; int valid;
char url[256]; char url[256];
char *sanetized_board_name, *image_name, *image_sha256, *tmp; char *sanetized_board_name, *image_name, *image_sha256, *tmp;
char *target_branch = NULL, *target_version = NULL, *target_fstype = NULL; char *cmd_target_branch = NULL, *cmd_target_version = NULL, *cmd_target_fstype = NULL;
struct blob_attr *tbr[__REPLY_MAX]; struct blob_attr *tbr[__REPLY_MAX];
struct blob_attr *tb[__TARGET_MAX] = {}; /* make sure tb is NULL initialized even if blobmsg_parse isn't called */ struct blob_attr *tb[__TARGET_MAX] = {}; /* make sure tb is NULL initialized even if blobmsg_parse isn't called */
struct stat imgstat; struct stat imgstat;
@ -1481,9 +1783,9 @@ int main(int args, char *argv[]) {
int revcmp; int revcmp;
int addargs; int addargs;
unsigned char argc = 1; unsigned char argc = 1;
bool force = false, use_get = false, in_queue = false, dont_ask = false, release_only = false; bool force = false, use_get = false, in_queue = false, release_only = false;
snprintf(user_agent, sizeof(user_agent), "%s (%s)", argv[0], AUC_VERSION); snprintf(user_agent, sizeof(user_agent), "%s/%s", argv[0], AUC_VERSION);
fprintf(stdout, "%s\n", user_agent); fprintf(stdout, "%s\n", user_agent);
while (argc<args) { while (argc<args) {
@ -1499,12 +1801,12 @@ int main(int args, char *argv[]) {
debug = 1; debug = 1;
#endif #endif
if (!strncmp(argv[argc], "-b", 3)) { if (!strncmp(argv[argc], "-b", 3)) {
target_branch = argv[argc + 1]; cmd_target_branch = argv[argc + 1];
addargs = 1; addargs = 1;
} }
if (!strncmp(argv[argc], "-B", 3)) { if (!strncmp(argv[argc], "-B", 3)) {
target_version = argv[argc + 1]; cmd_target_version = argv[argc + 1];
addargs = 1; addargs = 1;
} }
@ -1515,7 +1817,7 @@ int main(int args, char *argv[]) {
force = true; force = true;
if (!strncmp(argv[argc], "-F", 3)) { if (!strncmp(argv[argc], "-F", 3)) {
target_fstype = argv[argc + 1]; cmd_target_fstype = argv[argc + 1];
addargs = 1; addargs = 1;
} }
@ -1579,24 +1881,28 @@ int main(int args, char *argv[]) {
fprintf(stdout, "Server: %s\n", serverurl); fprintf(stdout, "Server: %s\n", serverurl);
fprintf(stdout, "Running: %s %s on %s (%s)\n", version, revision, target, board_name); fprintf(stdout, "Running: %s %s on %s (%s)\n", version, revision, target, board_name);
if (target_fstype && rootfs_type && strcmp(rootfs_type, target_fstype)) if (cmd_target_fstype && rootfs_type && strcmp(rootfs_type, cmd_target_fstype))
fprintf(stderr, "WARNING: will change rootfs type from '%s' to '%s'\n", fprintf(stderr, "WARNING: will change rootfs type from '%s' to '%s'\n",
rootfs_type, target_fstype); rootfs_type, cmd_target_fstype);
if (request_branches(!(target_branch || target_version))) { if (request_branches(!(cmd_target_branch || cmd_target_version))) {
rc=-ENODATA; rc=-ENODATA;
goto freeboard; goto freeboard;
} }
branch = select_branch(target_branch, target_version); running_branch = get_current_branch();
if (!branch) { if (!running_branch)
fprintf(stderr, "WARNING: cannot determing currently running branch.\n");
target_version = select_branch(cmd_target_branch, cmd_target_version);
if (!target_version) {
rc=-EINVAL; rc=-EINVAL;
goto freebranches; goto freebranches;
} }
fprintf(stdout, "Available: %s %s\n", branch->version_number, branch->version_code); fprintf(stdout, "Available: %s %s\n", target_version->version_number, target_version->version_code);
revcmp = verrevcmp(revision, branch->version_code); revcmp = verrevcmp(revision, target_version->version_code);
if (revcmp < 0) if (revcmp < 0)
upg_check |= PKG_UPGRADE; upg_check |= PKG_UPGRADE;
else if (revcmp > 0) else if (revcmp > 0)
@ -1604,16 +1910,29 @@ int main(int args, char *argv[]) {
if (release_only && !(upg_check & PKG_UPGRADE)) { if (release_only && !(upg_check & PKG_UPGRADE)) {
fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n"); fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n");
rc=0; rc = 0;
goto freebranches; goto freebranches;
} }
if ((rc = request_packages(branch))) running_revision = revision_from_version_code(revision);
if (target_version->branch == running_branch)
grab_changes(running_branch, running_revision);
else avl_for_element_range(running_branch, target_version->branch, current_branch, avl) {
if (current_branch == running_branch)
grab_changes(running_branch, running_revision);
else
grab_changes(current_branch, covered_revision);
if (current_branch->branch_off_rev > 0)
covered_revision = current_branch->branch_off_rev;
}
if ((rc = request_packages(target_version)))
goto freebranches; goto freebranches;
upg_check |= check_installed_packages(reqbuf.head); upg_check |= check_installed_packages();
if (upg_check & PKG_ERROR) { if (upg_check & PKG_ERROR) {
rc=-ENOPKG; rc = -ENOPKG;
goto freebranches; goto freebranches;
} }
@ -1625,13 +1944,13 @@ int main(int args, char *argv[]) {
if (!force && (upg_check & PKG_DOWNGRADE)) { if (!force && (upg_check & PKG_DOWNGRADE)) {
fprintf(stderr, "Refusing to downgrade. Use '-f' to force.\n"); fprintf(stderr, "Refusing to downgrade. Use '-f' to force.\n");
rc=-ENOTRECOVERABLE; rc = -ENOTRECOVERABLE;
goto freebranches; goto freebranches;
}; };
if (!force && (upg_check & PKG_NOT_FOUND)) { if (!force && (upg_check & PKG_NOT_FOUND)) {
fprintf(stderr, "Not all installed packages found in remote lists. Use '-f' to force.\n"); fprintf(stderr, "Not all installed packages found in remote lists. Use '-f' to force.\n");
rc=-ENOTRECOVERABLE; rc = -ENOTRECOVERABLE;
goto freebranches; goto freebranches;
}; };
@ -1639,17 +1958,18 @@ int main(int args, char *argv[]) {
goto freebranches; goto freebranches;
if (!dont_ask) { if (!dont_ask) {
rc = ask_user(); if (!ask_user("Are you sure you want to continue the upgrade process?")) {
if (rc) rc = 0;
goto freebranches; goto freebranches;
}
} }
blobmsg_add_string(&reqbuf, "version", branch->version); blobmsg_add_string(&reqbuf, "version", target_version->version);
blobmsg_add_string(&reqbuf, "version_code", branch->version_code); blobmsg_add_string(&reqbuf, "version_code", target_version->version_code);
blobmsg_add_string(&reqbuf, "target", target); blobmsg_add_string(&reqbuf, "target", target);
if (target_fstype || rootfs_type) if (cmd_target_fstype || rootfs_type)
blobmsg_add_string(&reqbuf, "filesystem", target_fstype?target_fstype:rootfs_type); blobmsg_add_string(&reqbuf, "filesystem", cmd_target_fstype?cmd_target_fstype:rootfs_type);
sanetized_board_name = strdup(board_name); sanetized_board_name = strdup(board_name);
tmp = sanetized_board_name; tmp = sanetized_board_name;
@ -1742,7 +2062,7 @@ int main(int args, char *argv[]) {
goto freebranches; goto freebranches;
} }
if ((rc = select_image(tb[TARGET_IMAGES], target_fstype, &image_name, &image_sha256))) if ((rc = select_image(tb[TARGET_IMAGES], cmd_target_fstype, &image_name, &image_sha256)))
goto freebranches; goto freebranches;
snprintf(url, sizeof(url), "%s/%s/%s/%s", serverurl, API_STORE, snprintf(url, sizeof(url), "%s/%s/%s/%s", serverurl, API_STORE,
@ -1809,6 +2129,7 @@ int main(int args, char *argv[]) {
sleep(10); sleep(10);
freebranches: freebranches:
free_branches();
if (rc && tb[TARGET_STDOUT] if (rc && tb[TARGET_STDOUT]
#ifdef AUC_DEBUG #ifdef AUC_DEBUG
&& !debug && !debug