summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Fahlgren2024-11-25 17:48:52 +0000
committerRobert Marko2024-12-02 10:25:24 +0000
commitcc9a471c32e106fa9ee045540613fefdc31c5cd2 (patch)
tree0a73940fb3034ac8c4210f5c340a726b024a2b40
parent9f4b86e70352ab9ca6aa272d096419acc53e2390 (diff)
downloadrpcd-cc9a471c32e106fa9ee045540613fefdc31c5cd2.tar.gz
rpc-sys: rework packagelist call for apk
Parse the apk files to produce the packagelist. Reads both the 'installed' file to collect all package names and versions. If the 'all' parameter is false, then only the packages listed in the 'world' file are retained. Remaining issue: There is no ABI version information in any of the apk files, so the returned file names have not been cleaned. This is contrary to the opkg version, which cleaned the package names, and is a bug that needs to be fixed once the ABI version are available. Fixes: openwrt/openwrt#16907 Fixes: efahl/owut#22 Signed-off-by: Eric Fahlgren <ericfahlgren@gmail.com> Link: https://github.com/openwrt/rpcd/pull/10 Signed-off-by: Robert Marko <robimarko@gmail.com>
-rw-r--r--sys.c141
1 files changed, 109 insertions, 32 deletions
diff --git a/sys.c b/sys.c
index da3508e..ec052d3 100644
--- a/sys.c
+++ b/sys.c
@@ -18,6 +18,7 @@
#include <stdbool.h>
#include <libubus.h>
+#include <sys/mman.h>
#include <rpcd/exec.h>
#include <rpcd/plugin.h>
@@ -166,9 +167,25 @@ rpc_cgi_password_set(struct ubus_context *ctx, struct ubus_object *obj,
}
static bool
-is_field(const char *type, const char *line)
+is_all_or_world(const char *pkg, const char **world)
{
- return strncmp(line, type, strlen(type)) == 0;
+ /* compares null-terminated pkg with non-null-terminated world[i] */
+ /* e.g., "my_pkg\0" == "my_pkg==1.2.3\n" => true */
+
+ if (!world) return true; /* handles 'all' case */
+
+ const char *terminators = "\n@~<>="; /* man 5 apk-world */
+
+ size_t i, c;
+ const char *item;
+ for (i = 0; *world[i]; i++) {
+ item = world[i];
+ for (c = 0; pkg[c] == item[c]; c++);
+ if (pkg[c] == '\0' && strchr(terminators, item[c]))
+ return true;
+ }
+
+ return false;
}
static bool
@@ -186,11 +203,24 @@ rpc_sys_packagelist(struct ubus_context *ctx, struct ubus_object *obj,
struct blob_attr *msg)
{
struct blob_attr *tb[__RPC_PACKAGELIST_MAX];
- bool all = false, installed = false, auto_installed = false;
+ bool all = false;
struct blob_buf buf = { 0 };
- char line[256], tmp[128], pkg[128], ver[128];
- char *pkg_abi;
+ char line[256], pkg[128], ver[128];
void *tbl;
+ struct stat statbuf;
+ const char **world = NULL;
+ char *world_mmap = NULL;
+ size_t world_mmap_size = 0;
+
+ /*
+ * Status file fields, /usr/lib/opkg/status vs /lib/apk/db/installed
+ * opkg apk
+ * PACKAGE_ABIVERSION "ABIVersion" no equivalent - see BUG, below
+ * PACKAGE_AUTOINSTALLED "Auto-Installed" package listed in 'world', not a db field
+ * PACKAGE_NAME "Package" "P"
+ * PACKAGE_STATUS "Status" package listed in db, not a status value
+ * PACKAGE_VERSION "Version" "V"
+ */
blobmsg_parse(rpc_packagelist_policy, __RPC_PACKAGELIST_MAX, tb,
blob_data(msg), blob_len(msg));
@@ -198,55 +228,102 @@ rpc_sys_packagelist(struct ubus_context *ctx, struct ubus_object *obj,
if (tb[RPC_PACKAGELIST_ALL] && blobmsg_get_bool(tb[RPC_PACKAGELIST_ALL]))
all = true;
- FILE *f = fopen("/usr/lib/opkg/status", "r");
+ FILE *f = fopen("/lib/apk/db/installed", "r");
if (!f)
return UBUS_STATUS_NOT_FOUND;
+ if (!all) {
+ /* We return only those items appearing in 'world' file. */
+ int world_fd = open("/etc/apk/world", O_RDONLY);
+ if (world_fd == -1)
+ return rpc_errno_status();
+
+ if (fstat(world_fd, &statbuf) == -1) {
+ close(world_fd);
+ return rpc_errno_status();
+ }
+
+ world_mmap_size = statbuf.st_size + 1;
+ if (world_mmap_size == 1) {
+ /* 'world' file is malformed: empty */
+ close(world_fd);
+ return UBUS_STATUS_UNKNOWN_ERROR;
+ }
+
+ world_mmap = (char *)mmap(NULL, world_mmap_size, PROT_READ, MAP_PRIVATE, world_fd, 0);
+ close(world_fd);
+ if (world_mmap == MAP_FAILED) {
+ return rpc_errno_status();
+ }
+
+ if (world_mmap[world_mmap_size-2] != '\n') {
+ /* 'world' file is malformed: missing final newline */
+ munmap(world_mmap, world_mmap_size);
+ return UBUS_STATUS_UNKNOWN_ERROR;
+ }
+
+ /* resulting 'world' pointer map looks like this:
+ * nstrs = 2 == count of newlines in mmap
+ * mmap = "pkg1\npkg2=1.2\n\0"
+ * | | |
+ * world[0]-+ | |
+ * world[1]-------+ |
+ * world[2]-----------------+
+ */
+
+ size_t istr, nstrs;
+ char *s;
+ for (nstrs = 0, s = world_mmap; s[nstrs]; s[nstrs] == '\n' ? nstrs++ : *s++);
+
+ if (nstrs) {
+ /* extra one in world for NULL sentinel */
+ world = (const char **)calloc(nstrs+1, sizeof(char *));
+ world[0] = world_mmap;
+ for (istr = 1, s = world_mmap; *s; s++) {
+ if (*s == '\n') {
+ world[istr] = s + 1;
+ istr++;
+ }
+ }
+ }
+ }
+
blob_buf_init(&buf, 0);
tbl = blobmsg_open_table(&buf, "packages");
pkg[0] = ver[0] = '\0';
while (fgets(line, sizeof(line), f)) {
switch (line[0]) {
- case 'A':
- if (is_field("ABIVersion", line)) {
- /* if there is ABIVersion, remove that suffix */
- if (sscanf(line, "ABIVersion: %127s", tmp) == 1
- && strlen(tmp) < strlen(pkg)) {
- pkg_abi = pkg + (strlen(pkg) - strlen(tmp));
- if (strncmp(pkg_abi, tmp, strlen(tmp)) == 0)
- pkg_abi[0] = '\0';
- }
- } else if (is_field("Auto-Installed", line))
- if (sscanf(line, "Auto-Installed: %63s", tmp) == 1)
- auto_installed = (strcmp(tmp, "yes") == 0);
- break;
case 'P':
- if (is_field("Package", line))
- if (sscanf(line, "Package: %127s", pkg) != 1)
- pkg[0] = '\0';
+ if (sscanf(line, "P: %127s", pkg) != 1)
+ pkg[0] = '\0';
break;
case 'V':
- if (is_field("Version", line))
- if (sscanf(line, "Version: %127s", ver) != 1)
- ver[0] = '\0';
- break;
- case 'S':
- if (is_field("Status", line))
- if (sscanf(line, "Status: install %63s installed", tmp) == 1)
- installed = true;
+ if (sscanf(line, "V: %127s", ver) != 1)
+ ver[0] = '\0';
break;
default:
if (is_blank(line)) {
- if (installed && (all || !auto_installed) && pkg[0] && ver[0])
+ if (pkg[0] && ver[0] && is_all_or_world(pkg, world)) {
+ /* BUG: There's no ABI version info in any of
+ * the apk files, so some of the returned file
+ * names contain ABI-versioning.
+ *
+ * If you had that information, you'd apply it here.
+ */
blobmsg_add_string(&buf, pkg, ver);
+ }
pkg[0] = ver[0] = '\0';
- installed = auto_installed = false;
}
break;
}
}
+ if (world)
+ free(world);
+ if (world_mmap)
+ munmap(world_mmap, world_mmap_size);
+
blobmsg_close_table(&buf, tbl);
ubus_send_reply(ctx, req, buf.head);
blob_buf_free(&buf);