This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new 2fcb2dfbd system/nxpkg: complete the package lifecycle
2fcb2dfbd is described below
commit 2fcb2dfbdd278ad88e303be9c86606ac32c4b77a
Author: aviralgarg05 <[email protected]>
AuthorDate: Sat Aug 15 16:03:15 2026 +0530
system/nxpkg: complete the package lifecycle
Add the remaining package commands. Order database and payload updates so
failures do not leave stale version pointers.
Signed-off-by: aviralgarg05 <[email protected]>
---
system/nxpkg/pkg_install.c | 640 ++++++++++++++++++++++++++++++++++++++-------
system/nxpkg/pkg_main.c | 99 +++++--
2 files changed, 628 insertions(+), 111 deletions(-)
diff --git a/system/nxpkg/pkg_install.c b/system/nxpkg/pkg_install.c
index b9b095597..db6e57597 100644
--- a/system/nxpkg/pkg_install.c
+++ b/system/nxpkg/pkg_install.c
@@ -25,10 +25,11 @@
****************************************************************************/
#include <errno.h>
-#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
#include <strings.h>
#include <string.h>
+#include <sys/stat.h>
#include <unistd.h>
#include "pkg.h"
@@ -37,58 +38,75 @@
* Private Functions
****************************************************************************/
-static int pkg_install_resolve_artifact(FAR char *buffer, size_t size,
- FAR const struct pkg_manifest_s
- *manifest)
+static int pkg_install_acquire_lock(FAR const char *name, FAR char *path,
+ size_t size)
{
int ret;
- if (manifest->artifact[0] == '/')
+ ret = pkg_store_ensure_package_root(name);
+ if (ret < 0)
{
- ret = snprintf(buffer, size, "%s", manifest->artifact);
- if (ret < 0)
- {
- return ret;
- }
-
- return (size_t)ret >= size ? -ENAMETOOLONG : 0;
+ return ret;
}
- ret = snprintf(buffer, size, "%s/%s", PKG_REPO_DIR, manifest->artifact);
+ ret = pkg_store_format_lock_path(path, size, name);
if (ret < 0)
{
return ret;
}
- return (size_t)ret >= size ? -ENAMETOOLONG : 0;
+ ret = pkg_lock_create(path);
+ if (ret == -EEXIST)
+ {
+ pkg_reclaim_stale_lock(path);
+ ret = pkg_lock_create(path);
+ }
+
+ return ret == -EEXIST ? -EBUSY : ret;
}
-static int pkg_install_acquire_lock(FAR const char *name, FAR char *path,
- size_t size)
+/****************************************************************************
+ * Name: pkg_install_acquire_installed_lock
+ *
+ * Description:
+ * Serialize installed database updates.
+ *
+ ****************************************************************************/
+
+static int pkg_install_acquire_installed_lock(FAR char *path, size_t size)
{
- int fd;
int ret;
+ int tries;
- ret = pkg_store_ensure_package_root(name);
+ ret = snprintf(path, size, PKG_ROOT_DIR "/instpkg.lk");
if (ret < 0)
{
return ret;
}
- ret = pkg_store_format_lock_path(path, size, name);
- if (ret < 0)
+ if ((size_t)ret >= size)
{
- return ret;
+ return -ENAMETOOLONG;
}
- fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
- if (fd < 0)
+ for (tries = 0; tries < 100; tries++)
{
- return errno == EEXIST ? -EBUSY : -errno;
+ ret = pkg_lock_create(path);
+ if (ret == 0)
+ {
+ return 0;
+ }
+
+ if (ret != -EEXIST)
+ {
+ return ret;
+ }
+
+ pkg_reclaim_stale_lock(path);
+ usleep(20 * 1000);
}
- close(fd);
- return 0;
+ return -EBUSY;
}
static bool pkg_install_has_version(
@@ -108,8 +126,49 @@ static bool pkg_install_has_version(
return false;
}
+static int pkg_install_prune_oldest_version(
+ FAR struct pkg_installed_entry_s *entry,
+ FAR char *pruned_version, size_t pruned_version_size)
+{
+ size_t victim = entry->version_count;
+ size_t i;
+
+ /* Keep the current and rollback versions when pruning. */
+
+ for (i = 0; i < entry->version_count; i++)
+ {
+ if (strcmp(entry->versions[i], entry->current) != 0 &&
+ strcmp(entry->versions[i], entry->previous) != 0)
+ {
+ victim = i;
+ break;
+ }
+ }
+
+ if (victim == entry->version_count)
+ {
+ return -E2BIG;
+ }
+
+ /* Let the caller delete this version after committing the database. */
+
+ snprintf(pruned_version, pruned_version_size, "%s",
+ entry->versions[victim]);
+
+ for (i = victim; i + 1 < entry->version_count; i++)
+ {
+ memcpy(entry->versions[i], entry->versions[i + 1],
+ sizeof(entry->versions[i]));
+ }
+
+ entry->version_count--;
+ return 0;
+}
+
static int pkg_install_add_version(FAR struct pkg_installed_entry_s *entry,
- FAR const char *version)
+ FAR const char *version,
+ FAR char *pruned_version,
+ size_t pruned_version_size)
{
int ret;
@@ -120,7 +179,12 @@ static int pkg_install_add_version(FAR struct
pkg_installed_entry_s *entry,
if (entry->version_count >= PKG_INSTALLED_VERSIONS_MAX)
{
- return -E2BIG;
+ ret = pkg_install_prune_oldest_version(entry, pruned_version,
+ pruned_version_size);
+ if (ret < 0)
+ {
+ return ret;
+ }
}
ret = snprintf(entry->versions[entry->version_count],
@@ -142,7 +206,9 @@ static int pkg_install_add_version(FAR struct
pkg_installed_entry_s *entry,
static int pkg_install_update_installed(FAR struct pkg_installed_db_s *db,
FAR const struct pkg_manifest_s
- *manifest)
+ *manifest,
+ FAR char *pruned_version,
+ size_t pruned_version_size)
{
FAR struct pkg_installed_entry_s *entry;
int ret;
@@ -196,12 +262,13 @@ static int pkg_install_update_installed(FAR struct
pkg_installed_db_s *db,
}
entry->type = manifest->type;
- return pkg_install_add_version(entry, manifest->version);
+ return pkg_install_add_version(entry, manifest->version, pruned_version,
+ pruned_version_size);
}
static int pkg_install_write_pointers(
FAR const struct pkg_installed_db_s *db,
- FAR const struct pkg_manifest_s *manifest)
+ FAR const char *name)
{
FAR struct pkg_installed_entry_s *entry;
char current[PATH_MAX];
@@ -209,21 +276,19 @@ static int pkg_install_write_pointers(
int ret;
entry = pkg_metadata_find_installed((FAR struct pkg_installed_db_s *)db,
- manifest->name);
+ name);
if (entry == NULL)
{
return -ENOENT;
}
- ret = pkg_store_format_current_path(current, sizeof(current),
- manifest->name);
+ ret = pkg_store_format_current_path(current, PATH_MAX, name);
if (ret < 0)
{
return ret;
}
- ret = pkg_store_format_previous_path(previous, sizeof(previous),
- manifest->name);
+ ret = pkg_store_format_previous_path(previous, PATH_MAX, name);
if (ret < 0)
{
return ret;
@@ -247,185 +312,286 @@ int pkg_install(FAR const char *name)
FAR struct pkg_index_s *index;
FAR struct pkg_installed_db_s *installed;
FAR const struct pkg_manifest_s *manifest;
- char source[PATH_MAX];
- char tmp[PATH_MAX] = "";
- char payload[PATH_MAX];
- char manifest_path[PATH_MAX];
- char lock[PATH_MAX] = "";
+ FAR char *source;
+ FAR char *tmp;
+ FAR char *payload;
+ FAR char *manifest_path;
+ FAR char *lock;
+ FAR char *installed_lock;
+ FAR const char *artifact;
char digest[PKG_HASH_HEX_LEN + 1];
+ char pruned_version[PKG_VERSION_MAX + 1];
+ bool staged_to_tmp;
+ bool version_dir_created;
+ bool installed_lock_held;
int ret;
- index = malloc(sizeof(*index));
- installed = malloc(sizeof(*installed));
- if (index == NULL || installed == NULL)
+ pruned_version[0] = '\0';
+
+ index = pkg_zalloc(sizeof(*index));
+ installed = pkg_zalloc(sizeof(*installed));
+ source = pkg_malloc(PATH_MAX);
+ tmp = pkg_malloc(PATH_MAX);
+ payload = pkg_malloc(PATH_MAX);
+ manifest_path = pkg_malloc(PATH_MAX);
+ lock = pkg_malloc(PATH_MAX);
+ installed_lock = pkg_malloc(PATH_MAX);
+ if (index == NULL || installed == NULL || source == NULL || tmp == NULL ||
+ payload == NULL || manifest_path == NULL || lock == NULL ||
+ installed_lock == NULL)
{
- free(index);
- free(installed);
pkg_error("unable to allocate package metadata buffers");
- return EXIT_FAILURE;
+ goto errout_early;
}
+ source[0] = '\0';
+ tmp[0] = '\0';
+ payload[0] = '\0';
+ manifest_path[0] = '\0';
+ lock[0] = '\0';
+ installed_lock[0] = '\0';
+ installed_lock_held = false;
+ artifact = NULL;
+ staged_to_tmp = false;
+ version_dir_created = false;
+
ret = pkg_store_prepare_layout();
if (ret < 0)
{
- free(index);
- free(installed);
pkg_error("unable to prepare package layout: %d", ret);
- return EXIT_FAILURE;
+ goto errout_early;
}
ret = pkg_metadata_load_index(index);
if (ret < 0)
{
- free(index);
- free(installed);
pkg_error("unable to load local index metadata: %d", ret);
- return EXIT_FAILURE;
+ goto errout_early;
}
manifest = pkg_metadata_find_latest(index, name);
if (manifest == NULL)
{
- free(index);
- free(installed);
pkg_error("package '%s' not found in local index", name);
- return EXIT_FAILURE;
+ goto errout_early;
}
- ret = pkg_install_resolve_artifact(source, sizeof(source), manifest);
+ ret = pkg_resolve_artifact_source(source, PATH_MAX, manifest);
if (ret < 0)
{
- pkg_error("artifact path for '%s' is too long", name);
- return EXIT_FAILURE;
+ pkg_error("unable to resolve artifact source for '%s': %d", name, ret);
+ goto errout_early;
}
- ret = pkg_install_acquire_lock(name, lock, sizeof(lock));
+ ret = pkg_install_acquire_lock(name, lock, PATH_MAX);
if (ret < 0)
{
pkg_error("unable to acquire package lock for '%s': %d", name, ret);
- return EXIT_FAILURE;
+ goto errout_early;
}
ret = pkg_txn_write_state(name, PKG_TXN_FETCHING);
if (ret < 0)
{
+ pkg_error("txn state fetching failed: %d", ret);
goto errout;
}
- ret = pkg_store_format_download_path(tmp, sizeof(tmp), manifest->name,
- manifest->version);
- if (ret < 0)
+ if (pkg_source_is_url(source))
{
- goto errout;
- }
+ ret = pkg_store_format_download_path(tmp, PATH_MAX, manifest->name,
+ manifest->version);
+ if (ret < 0)
+ {
+ pkg_error("download path format failed: %d", ret);
+ goto errout;
+ }
- ret = pkg_store_copy_file(source, tmp);
- if (ret < 0)
+ ret = pkg_acquire_source(source, tmp);
+ if (ret < 0)
+ {
+ pkg_error("acquire source failed: %d", ret);
+ goto errout;
+ }
+
+ artifact = tmp;
+ staged_to_tmp = true;
+ }
+ else
{
- goto errout;
+ artifact = source;
}
- ret = pkg_hash_file_sha256(tmp, digest);
+ ret = pkg_hash_file_sha256(artifact, digest);
if (ret < 0)
{
+ pkg_error("sha256 failed: %d", ret);
goto errout;
}
if (strcasecmp(digest, manifest->sha256) != 0)
{
ret = -EILSEQ;
+ pkg_error("sha256 mismatch: %d", ret);
goto errout;
}
ret = pkg_txn_write_state(name, PKG_TXN_VERIFIED);
if (ret < 0)
{
+ pkg_error("txn state verified failed: %d", ret);
+ goto errout;
+ }
+
+ ret = pkg_store_format_version_path(payload, PATH_MAX, manifest->name,
+ manifest->version);
+ if (ret < 0)
+ {
+ pkg_error("version path format failed: %d", ret);
+ goto errout;
+ }
+
+ if (access(payload, F_OK) == 0)
+ {
+ version_dir_created = false;
+ }
+ else if (errno == ENOENT)
+ {
+ version_dir_created = true;
+ }
+ else
+ {
+ ret = -errno;
+ pkg_error("unable to inspect version directory: %d", ret);
goto errout;
}
ret = pkg_store_ensure_version_dir(manifest->name, manifest->version);
if (ret < 0)
{
+ pkg_error("ensure version dir failed: %d", ret);
goto errout;
}
- ret = pkg_store_format_payload_path(payload, sizeof(payload),
+ ret = pkg_store_format_payload_path(payload, PATH_MAX,
manifest->name, manifest->version,
manifest->artifact);
if (ret < 0)
{
+ pkg_error("payload path format failed: %d", ret);
goto errout;
}
- ret = pkg_store_copy_file(tmp, payload);
+ ret = pkg_store_copy_file(artifact, payload);
if (ret < 0)
{
+ pkg_error("copy payload failed: %d", ret);
+ goto errout;
+ }
+
+ if (manifest->type == PKG_PAYLOAD_ELF &&
+ chmod(payload, 0755) < 0 && errno != ENOSYS)
+ {
+ ret = -errno;
+ pkg_error("mark payload executable failed: %d", ret);
goto errout;
}
- ret = pkg_store_format_manifest_path(manifest_path, sizeof(manifest_path),
+ ret = pkg_store_format_manifest_path(manifest_path, PATH_MAX,
manifest->name, manifest->version);
if (ret < 0)
{
+ pkg_error("manifest path format failed: %d", ret);
goto errout;
}
ret = pkg_metadata_write_manifest(manifest_path, manifest);
if (ret < 0)
{
+ pkg_error("write manifest failed: %d", ret);
goto errout;
}
ret = pkg_txn_write_state(name, PKG_TXN_STAGED);
if (ret < 0)
{
+ pkg_error("txn state staged failed: %d", ret);
goto errout;
}
ret = pkg_compat_check(manifest);
if (ret < 0)
{
+ pkg_error("compat check failed: %d", ret);
goto errout;
}
ret = pkg_txn_write_state(name, PKG_TXN_COMPAT_OK);
if (ret < 0)
{
+ pkg_error("txn state compat_ok failed: %d", ret);
goto errout;
}
- ret = pkg_metadata_load_installed(installed);
+ ret = pkg_install_acquire_installed_lock(installed_lock, PATH_MAX);
if (ret < 0)
{
+ pkg_error("unable to acquire installed-db lock: %d", ret);
goto errout;
}
- ret = pkg_install_update_installed(installed, manifest);
+ installed_lock_held = true;
+
+ ret = pkg_metadata_load_installed(installed);
if (ret < 0)
{
+ pkg_error("load installed metadata failed: %d", ret);
goto errout;
}
- ret = pkg_install_write_pointers(installed, manifest);
+ ret = pkg_install_update_installed(installed, manifest, pruned_version,
+ sizeof(pruned_version));
if (ret < 0)
{
+ pkg_error("update installed metadata failed: %d", ret);
goto errout;
}
ret = pkg_metadata_save_installed(installed);
if (ret < 0)
{
+ pkg_error("save installed metadata failed: %d", ret);
goto errout;
}
+ /* Remove the pruned payload after committing the database. */
+
+ if (pruned_version[0] != '\0')
+ {
+ pkg_store_remove_version_dir(manifest->name, pruned_version);
+ }
+
+ ret = pkg_install_write_pointers(installed, manifest->name);
+ if (ret < 0)
+ {
+ /* Pointer files can be rebuilt from the installed database. */
+
+ pkg_error("unable to refresh current/previous pointers: %d", ret);
+ }
+
+ pkg_store_remove_file(installed_lock);
+ installed_lock_held = false;
+
ret = pkg_txn_write_state(name, PKG_TXN_ACTIVATED);
if (ret < 0)
{
- goto errout;
+ /* Do not remove payloads after the database commit. */
+
+ pkg_error("txn state activated failed: %d", ret);
}
pkg_txn_write_state(name, PKG_TXN_CLEANUP);
- if (tmp[0] != '\0')
+ if (staged_to_tmp && tmp[0] != '\0')
{
pkg_store_remove_file(tmp);
}
@@ -437,27 +603,53 @@ int pkg_install(FAR const char *name)
}
pkg_info("installed %s version %s", manifest->name, manifest->version);
- free(index);
- free(installed);
- return EXIT_SUCCESS;
+ ret = EXIT_SUCCESS;
+ goto freeout;
errout:
pkg_txn_write_state(name, PKG_TXN_FAILED);
- if (tmp[0] != '\0')
+ if (staged_to_tmp && tmp[0] != '\0')
{
pkg_store_remove_file(tmp);
}
+ /* Remove a version directory created by this failed install. */
+
+ if (version_dir_created)
+ {
+ pkg_store_remove_version_dir(manifest->name, manifest->version);
+ }
+
pkg_txn_clear_state(name);
if (lock[0] != '\0')
{
pkg_lock_remove(lock);
}
- free(index);
- free(installed);
+ if (installed_lock_held)
+ {
+ pkg_store_remove_file(installed_lock);
+ }
+
pkg_error("install failed for '%s': %d", name, ret);
- return EXIT_FAILURE;
+ goto freeout;
+
+errout_early:
+ ret = EXIT_FAILURE;
+
+freeout:
+ pkg_free(index);
+ pkg_free(installed);
+ pkg_free(source);
+ pkg_free(tmp);
+ pkg_free(payload);
+ pkg_free(manifest_path);
+ pkg_free(lock);
+ pkg_free(installed_lock);
+
+ /* Preserve negative errno values for library callers. */
+
+ return ret;
}
int pkg_list(FAR FILE *stream)
@@ -465,7 +657,7 @@ int pkg_list(FAR FILE *stream)
FAR struct pkg_installed_db_s *db;
int ret;
- db = malloc(sizeof(*db));
+ db = pkg_zalloc(sizeof(*db));
if (db == NULL)
{
pkg_error("unable to allocate installed metadata buffer");
@@ -475,7 +667,7 @@ int pkg_list(FAR FILE *stream)
ret = pkg_store_prepare_layout();
if (ret < 0)
{
- free(db);
+ pkg_free(db);
pkg_error("unable to prepare package layout: %d", ret);
return EXIT_FAILURE;
}
@@ -483,7 +675,7 @@ int pkg_list(FAR FILE *stream)
ret = pkg_metadata_load_installed(db);
if (ret < 0)
{
- free(db);
+ pkg_free(db);
pkg_error("unable to load installed metadata: %d", ret);
return EXIT_FAILURE;
}
@@ -491,11 +683,279 @@ int pkg_list(FAR FILE *stream)
ret = pkg_metadata_print_installed(stream, db);
if (ret < 0)
{
- free(db);
+ pkg_free(db);
pkg_error("unable to print installed metadata: %d", ret);
return EXIT_FAILURE;
}
- free(db);
+ pkg_free(db);
+ return EXIT_SUCCESS;
+}
+
+/****************************************************************************
+ * Name: pkg_uninstall
+ *
+ * Description:
+ * Remove a package and all of its installed versions.
+ *
+ ****************************************************************************/
+
+int pkg_uninstall(FAR const char *name)
+{
+ FAR struct pkg_installed_db_s *db;
+ FAR struct pkg_installed_entry_s *entry;
+ struct pkg_installed_entry_s removed;
+ char path[PATH_MAX];
+ char package_lock[PATH_MAX];
+ char installed_lock[PATH_MAX];
+ size_t index;
+ size_t i;
+ int ret;
+
+ if (!pkg_validate_path_component(name))
+ {
+ pkg_error("remove requires a valid package name");
+ return EXIT_FAILURE;
+ }
+
+ db = pkg_zalloc(sizeof(*db));
+ if (db == NULL)
+ {
+ pkg_error("unable to allocate installed metadata buffer");
+ return EXIT_FAILURE;
+ }
+
+ ret = pkg_store_prepare_layout();
+ if (ret < 0)
+ {
+ pkg_error("unable to prepare package layout: %d", ret);
+ goto errout_with_db;
+ }
+
+ ret = pkg_install_acquire_lock(name, package_lock, sizeof(package_lock));
+ if (ret < 0)
+ {
+ pkg_error("unable to acquire package lock for '%s': %d", name, ret);
+ goto errout_with_db;
+ }
+
+ ret = pkg_install_acquire_installed_lock(installed_lock,
+ sizeof(installed_lock));
+ if (ret < 0)
+ {
+ pkg_error("unable to acquire installed-db lock: %d", ret);
+ goto errout_with_package_lock;
+ }
+
+ ret = pkg_metadata_load_installed(db);
+ if (ret < 0)
+ {
+ pkg_error("unable to load installed metadata: %d", ret);
+ goto errout_with_installed_lock;
+ }
+
+ entry = pkg_metadata_find_installed(db, name);
+ if (entry == NULL)
+ {
+ pkg_error("package '%s' is not installed", name);
+ goto errout_with_installed_lock;
+ }
+
+ /* Commit removal before deleting payloads. */
+
+ removed = *entry;
+ index = (size_t)(entry - db->entries);
+ for (i = index; i + 1 < db->count; i++)
+ {
+ db->entries[i] = db->entries[i + 1];
+ }
+
+ db->count--;
+
+ ret = pkg_metadata_save_installed(db);
+ pkg_store_remove_file(installed_lock);
+ if (ret < 0)
+ {
+ pkg_error("unable to save installed metadata: %d", ret);
+ goto errout_with_package_lock;
+ }
+
+ for (i = 0; i < removed.version_count; i++)
+ {
+ pkg_store_remove_version_dir(name, removed.versions[i]);
+ }
+
+ if (pkg_store_format_txn_path(path, sizeof(path), name) == 0)
+ {
+ pkg_store_remove_file(path);
+ }
+
+ if (pkg_store_format_current_path(path, sizeof(path), name) == 0)
+ {
+ pkg_store_remove_file(path);
+ }
+
+ if (pkg_store_format_previous_path(path, sizeof(path), name) == 0)
+ {
+ pkg_store_remove_file(path);
+ }
+
+ pkg_store_remove_file(package_lock);
+
+ if (pkg_store_format_package_root(path, sizeof(path), name) == 0)
+ {
+ rmdir(path);
+ }
+
+ pkg_info("removed %s", name);
+ pkg_free(db);
+ return EXIT_SUCCESS;
+
+errout_with_installed_lock:
+ pkg_store_remove_file(installed_lock);
+
+errout_with_package_lock:
+ pkg_store_remove_file(package_lock);
+
+errout_with_db:
+ pkg_free(db);
+ return EXIT_FAILURE;
+}
+
+/****************************************************************************
+ * Name: pkg_rollback
+ *
+ * Description:
+ * Swap the current and previous installed versions.
+ *
+ ****************************************************************************/
+
+int pkg_rollback(FAR const char *name)
+{
+ FAR struct pkg_installed_db_s *db;
+ FAR struct pkg_installed_entry_s *entry;
+ char version_path[PATH_MAX];
+ char package_lock[PATH_MAX];
+ char installed_lock[PATH_MAX];
+ char swap[PKG_VERSION_MAX + 1];
+ struct stat st;
+ int ret;
+
+ if (!pkg_validate_path_component(name))
+ {
+ pkg_error("rollback requires a valid package name");
+ return EXIT_FAILURE;
+ }
+
+ db = pkg_zalloc(sizeof(*db));
+ if (db == NULL)
+ {
+ pkg_error("unable to allocate installed metadata buffer");
+ return EXIT_FAILURE;
+ }
+
+ ret = pkg_store_prepare_layout();
+ if (ret < 0)
+ {
+ pkg_error("unable to prepare package layout: %d", ret);
+ goto errout_with_db;
+ }
+
+ ret = pkg_install_acquire_lock(name, package_lock, sizeof(package_lock));
+ if (ret < 0)
+ {
+ pkg_error("unable to acquire package lock for '%s': %d", name, ret);
+ goto errout_with_db;
+ }
+
+ ret = pkg_install_acquire_installed_lock(installed_lock,
+ sizeof(installed_lock));
+ if (ret < 0)
+ {
+ pkg_error("unable to acquire installed-db lock: %d", ret);
+ goto errout_with_package_lock;
+ }
+
+ ret = pkg_metadata_load_installed(db);
+ if (ret < 0)
+ {
+ pkg_error("unable to load installed metadata: %d", ret);
+ goto errout_with_installed_lock;
+ }
+
+ entry = pkg_metadata_find_installed(db, name);
+ if (entry == NULL)
+ {
+ pkg_error("package '%s' is not installed", name);
+ goto errout_with_installed_lock;
+ }
+
+ if (entry->previous[0] == '\0')
+ {
+ pkg_error("package '%s' has no previous version to roll back to",
+ name);
+ goto errout_with_installed_lock;
+ }
+
+ ret = pkg_store_format_version_path(version_path, sizeof(version_path),
+ name, entry->previous);
+ if (ret < 0 || stat(version_path, &st) < 0)
+ {
+ pkg_error("rollback target version '%s' is missing on disk",
+ entry->previous);
+ goto errout_with_installed_lock;
+ }
+
+ ret = snprintf(swap, sizeof(swap), "%s", entry->current);
+ if (ret < 0 || (size_t)ret >= sizeof(swap))
+ {
+ pkg_error("current version string too long to swap");
+ goto errout_with_installed_lock;
+ }
+
+ ret = snprintf(entry->current, sizeof(entry->current), "%s",
+ entry->previous);
+ if (ret < 0 || (size_t)ret >= sizeof(entry->current))
+ {
+ pkg_error("unable to update current version");
+ goto errout_with_installed_lock;
+ }
+
+ ret = snprintf(entry->previous, sizeof(entry->previous), "%s", swap);
+ if (ret < 0 || (size_t)ret >= sizeof(entry->previous))
+ {
+ pkg_error("unable to update previous version");
+ goto errout_with_installed_lock;
+ }
+
+ /* Commit the database before refreshing pointer files. */
+
+ ret = pkg_metadata_save_installed(db);
+ if (ret < 0)
+ {
+ pkg_error("unable to save installed metadata: %d", ret);
+ goto errout_with_installed_lock;
+ }
+
+ ret = pkg_install_write_pointers(db, name);
+ pkg_store_remove_file(installed_lock);
+ pkg_store_remove_file(package_lock);
+ if (ret < 0)
+ {
+ pkg_error("unable to refresh current/previous pointers: %d", ret);
+ }
+
+ pkg_info("rolled back %s to version %s", name, entry->current);
+ pkg_free(db);
return EXIT_SUCCESS;
+
+errout_with_installed_lock:
+ pkg_store_remove_file(installed_lock);
+
+errout_with_package_lock:
+ pkg_store_remove_file(package_lock);
+
+errout_with_db:
+ pkg_free(db);
+ return EXIT_FAILURE;
}
diff --git a/system/nxpkg/pkg_main.c b/system/nxpkg/pkg_main.c
index c049592c5..3755e1231 100644
--- a/system/nxpkg/pkg_main.c
+++ b/system/nxpkg/pkg_main.c
@@ -29,8 +29,18 @@
#include <stdlib.h>
#include <string.h>
+#include <netutils/cJSON.h>
+
#include "pkg.h"
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define PKG_USAGE \
+ "Usage: %s <install|update|remove|rollback|list|available|sync|help> " \
+ "[args]\n"
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -38,12 +48,15 @@
int main(int argc, FAR char *argv[])
{
FAR const char *cmd;
+ cJSON_Hooks hooks;
+
+ hooks.malloc_fn = malloc;
+ hooks.free_fn = free;
+ cJSON_InitHooks(&hooks);
if (argc < 2)
{
- fprintf(stderr,
- "Usage: %s <install|update|list|rollback|help> [args]\n",
- argv[0]);
+ fprintf(stderr, PKG_USAGE, argv[0]);
return EXIT_FAILURE;
}
@@ -52,9 +65,7 @@ int main(int argc, FAR char *argv[])
if (strcmp(cmd, "help") == 0 || strcmp(cmd, "--help") == 0 ||
strcmp(cmd, "-h") == 0)
{
- fprintf(stdout,
- "Usage: %s <install|update|list|rollback|help> [args]\n",
- argv[0]);
+ fprintf(stdout, PKG_USAGE, argv[0]);
return EXIT_SUCCESS;
}
@@ -63,19 +74,51 @@ int main(int argc, FAR char *argv[])
if (argc != 3)
{
pkg_error("install expects exactly one package name");
- fprintf(stderr,
- "Usage: %s <install|update|list|rollback|help> [args]\n",
- argv[0]);
+ fprintf(stderr, PKG_USAGE, argv[0]);
return EXIT_FAILURE;
}
- return pkg_install(argv[2]);
+ /* Convert library errors to a shell exit status. */
+
+ return pkg_install(argv[2]) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
+ /* Installing the latest indexed version also performs an update. */
+
if (strcmp(cmd, "update") == 0)
{
- pkg_error("'update' is not implemented yet in the current unit");
- return EXIT_FAILURE;
+ if (argc != 3)
+ {
+ pkg_error("update expects exactly one package name");
+ fprintf(stderr, PKG_USAGE, argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ return pkg_install(argv[2]) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
+
+ if (strcmp(cmd, "remove") == 0 || strcmp(cmd, "uninstall") == 0)
+ {
+ if (argc != 3)
+ {
+ pkg_error("remove expects exactly one package name");
+ fprintf(stderr, PKG_USAGE, argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ return pkg_uninstall(argv[2]);
+ }
+
+ if (strcmp(cmd, "rollback") == 0)
+ {
+ if (argc != 3)
+ {
+ pkg_error("rollback expects exactly one package name");
+ fprintf(stderr, PKG_USAGE, argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ return pkg_rollback(argv[2]);
}
if (strcmp(cmd, "list") == 0)
@@ -83,24 +126,38 @@ int main(int argc, FAR char *argv[])
if (argc != 2)
{
pkg_error("list does not take additional arguments");
- fprintf(stderr,
- "Usage: %s <install|update|list|rollback|help> [args]\n",
- argv[0]);
+ fprintf(stderr, PKG_USAGE, argv[0]);
return EXIT_FAILURE;
}
return pkg_list(stdout);
}
- if (strcmp(cmd, "rollback") == 0)
+ if (strcmp(cmd, "available") == 0)
{
- pkg_error("'rollback' is not implemented yet in the current unit");
- return EXIT_FAILURE;
+ if (argc != 2)
+ {
+ pkg_error("available does not take additional arguments");
+ fprintf(stderr, PKG_USAGE, argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ return pkg_available(stdout);
+ }
+
+ if (strcmp(cmd, "sync") == 0)
+ {
+ if (argc != 3)
+ {
+ pkg_error("sync expects exactly one index source");
+ fprintf(stderr, PKG_USAGE, argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ return pkg_sync(argv[2]) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
fprintf(stderr, "ERROR: Unknown subcommand '%s'\n", cmd);
- fprintf(stderr,
- "Usage: %s <install|update|list|rollback|help> [args]\n",
- argv[0]);
+ fprintf(stderr, PKG_USAGE, argv[0]);
return EXIT_FAILURE;
}