From 075c826b3314278fcdaf8f1af764d67fe29150b9 Mon Sep 17 00:00:00 2001 From: aviralgarg05 Date: Tue, 5 May 2026 21:26:01 +0530 Subject: [PATCH 1/6] system/pkg: add lifecycle helper skeleton --- system/pkg/CMakeLists.txt | 35 ++++++++++++++ system/pkg/Kconfig | 30 ++++++++++++ system/pkg/Make.defs | 25 ++++++++++ system/pkg/Makefile | 32 +++++++++++++ system/pkg/pkg_main.c | 97 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 system/pkg/CMakeLists.txt create mode 100644 system/pkg/Kconfig create mode 100644 system/pkg/Make.defs create mode 100644 system/pkg/Makefile create mode 100644 system/pkg/pkg_main.c diff --git a/system/pkg/CMakeLists.txt b/system/pkg/CMakeLists.txt new file mode 100644 index 00000000000..af40caa7a0a --- /dev/null +++ b/system/pkg/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/system/pkg/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_PKG) + nuttx_add_application( + MODULE + ${CONFIG_SYSTEM_PKG} + NAME + ${CONFIG_SYSTEM_PKG_PROGNAME} + STACKSIZE + ${CONFIG_SYSTEM_PKG_STACKSIZE} + PRIORITY + ${CONFIG_SYSTEM_PKG_PRIORITY} + SRCS + pkg_main.c) +endif() diff --git a/system/pkg/Kconfig b/system/pkg/Kconfig new file mode 100644 index 00000000000..f73af71d4ba --- /dev/null +++ b/system/pkg/Kconfig @@ -0,0 +1,30 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_PKG + tristate "'pkg' lifecycle helper" + default n + ---help--- + Enable the thin package lifecycle helper proposed for the + Dynamic ELF distribution work. + +if SYSTEM_PKG + +config SYSTEM_PKG_PROGNAME + string "Program name" + default "pkg" + ---help--- + This is the name of the program that will be used when the + pkg tool is installed. + +config SYSTEM_PKG_PRIORITY + int "'pkg' task priority" + default 100 + +config SYSTEM_PKG_STACKSIZE + int "'pkg' stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/system/pkg/Make.defs b/system/pkg/Make.defs new file mode 100644 index 00000000000..a69b3c46283 --- /dev/null +++ b/system/pkg/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/system/pkg/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_PKG),) +CONFIGURED_APPS += $(APPDIR)/system/pkg +endif diff --git a/system/pkg/Makefile b/system/pkg/Makefile new file mode 100644 index 00000000000..66e80f9815d --- /dev/null +++ b/system/pkg/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/system/pkg/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_SYSTEM_PKG_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_PKG_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_PKG_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_PKG) + +MAINSRC = pkg_main.c + +include $(APPDIR)/Application.mk diff --git a/system/pkg/pkg_main.c b/system/pkg/pkg_main.c new file mode 100644 index 00000000000..3a1eb5dbad4 --- /dev/null +++ b/system/pkg/pkg_main.c @@ -0,0 +1,97 @@ +/**************************************************************************** + * apps/system/pkg/pkg_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void pkg_show_usage(FAR FILE *stream, FAR const char *progname) +{ + fprintf(stream, + "Usage: %s [args]\n", + progname); +} + +static int pkg_not_implemented(FAR const char *cmd) +{ + fprintf(stderr, + "ERROR: 'pkg %s' is not implemented yet in the current unit.\n", + cmd); + return EXIT_FAILURE; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR const char *cmd; + + if (argc < 2) + { + pkg_show_usage(stderr, argv[0]); + return EXIT_FAILURE; + } + + cmd = argv[1]; + + if (strcmp(cmd, "help") == 0 || strcmp(cmd, "--help") == 0 || + strcmp(cmd, "-h") == 0) + { + pkg_show_usage(stdout, argv[0]); + return EXIT_SUCCESS; + } + + if (strcmp(cmd, "install") == 0) + { + return pkg_not_implemented("install"); + } + + if (strcmp(cmd, "update") == 0) + { + return pkg_not_implemented("update"); + } + + if (strcmp(cmd, "list") == 0) + { + return pkg_not_implemented("list"); + } + + if (strcmp(cmd, "rollback") == 0) + { + return pkg_not_implemented("rollback"); + } + + fprintf(stderr, "ERROR: Unknown subcommand '%s'\n", cmd); + pkg_show_usage(stderr, argv[0]); + return EXIT_FAILURE; +} From e260c75e8cdee2d172d4010c47561f1f45506a55 Mon Sep 17 00:00:00 2001 From: aviralgarg05 Date: Wed, 6 May 2026 20:45:42 +0530 Subject: [PATCH 2/6] system/pkg: add metadata and store foundation --- system/pkg/CMakeLists.txt | 6 +- system/pkg/Makefile | 1 + system/pkg/pkg.h | 117 ++++++++++++++++++++++++++ system/pkg/pkg_log.c | 64 ++++++++++++++ system/pkg/pkg_main.c | 6 +- system/pkg/pkg_manifest.c | 130 ++++++++++++++++++++++++++++ system/pkg/pkg_store.c | 173 ++++++++++++++++++++++++++++++++++++++ system/pkg/pkg_txn.c | 67 +++++++++++++++ 8 files changed, 560 insertions(+), 4 deletions(-) create mode 100644 system/pkg/pkg.h create mode 100644 system/pkg/pkg_log.c create mode 100644 system/pkg/pkg_manifest.c create mode 100644 system/pkg/pkg_store.c create mode 100644 system/pkg/pkg_txn.c diff --git a/system/pkg/CMakeLists.txt b/system/pkg/CMakeLists.txt index af40caa7a0a..2116efab5e7 100644 --- a/system/pkg/CMakeLists.txt +++ b/system/pkg/CMakeLists.txt @@ -31,5 +31,9 @@ if(CONFIG_SYSTEM_PKG) PRIORITY ${CONFIG_SYSTEM_PKG_PRIORITY} SRCS - pkg_main.c) + pkg_main.c + pkg_log.c + pkg_manifest.c + pkg_store.c + pkg_txn.c) endif() diff --git a/system/pkg/Makefile b/system/pkg/Makefile index 66e80f9815d..6020ad557aa 100644 --- a/system/pkg/Makefile +++ b/system/pkg/Makefile @@ -27,6 +27,7 @@ PRIORITY = $(CONFIG_SYSTEM_PKG_PRIORITY) STACKSIZE = $(CONFIG_SYSTEM_PKG_STACKSIZE) MODULE = $(CONFIG_SYSTEM_PKG) +CSRCS = pkg_log.c pkg_manifest.c pkg_store.c pkg_txn.c MAINSRC = pkg_main.c include $(APPDIR)/Application.mk diff --git a/system/pkg/pkg.h b/system/pkg/pkg.h new file mode 100644 index 00000000000..8ae8758d0cf --- /dev/null +++ b/system/pkg/pkg.h @@ -0,0 +1,117 @@ +/**************************************************************************** + * apps/system/pkg/pkg.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_SYSTEM_PKG_PKG_H +#define __APPS_SYSTEM_PKG_PKG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PKG_REPO_DIR "/data/repo" +#define PKG_REPO_INDEX "/data/repo/index.json" +#define PKG_REPO_INSTALLED "/data/repo/installed.json" +#define PKG_STORE_DIR "/data/pkgs" +#define PKG_TMP_DIR "/data/tmp" +#define PKG_TMP_PKG_DIR "/data/tmp/pkg" + +#define PKG_NAME_MAX 63 +#define PKG_VERSION_MAX 31 +#define PKG_ARCH_MAX 31 +#define PKG_COMPAT_MAX 63 +#define PKG_HASH_HEX_LEN 64 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum pkg_payload_type_e +{ + PKG_PAYLOAD_ELF = 0, + PKG_PAYLOAD_SHARED_LIB +}; + +enum pkg_txn_state_e +{ + PKG_TXN_IDLE = 0, + PKG_TXN_FETCHING, + PKG_TXN_VERIFIED, + PKG_TXN_STAGED, + PKG_TXN_COMPAT_OK, + PKG_TXN_ACTIVATED, + PKG_TXN_CLEANUP, + PKG_TXN_FAILED, + PKG_TXN_RESTORE +}; + +struct pkg_manifest_s +{ + char name[PKG_NAME_MAX + 1]; + char version[PKG_VERSION_MAX + 1]; + char arch[PKG_ARCH_MAX + 1]; + char compat[PKG_COMPAT_MAX + 1]; + char artifact[PATH_MAX]; + char sha256[PKG_HASH_HEX_LEN + 1]; + enum pkg_payload_type_e type; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +const char *pkg_manifest_type_str(enum pkg_payload_type_e type); +int pkg_manifest_validate(FAR const struct pkg_manifest_s *manifest); + +int pkg_store_prepare_layout(void); +int pkg_store_format_index_path(FAR char *buffer, size_t size); +int pkg_store_format_installed_path(FAR char *buffer, size_t size); +int pkg_store_format_package_root(FAR char *buffer, size_t size, + FAR const char *name); +int pkg_store_format_version_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version); +int pkg_store_format_current_path(FAR char *buffer, size_t size, + FAR const char *name); +int pkg_store_format_previous_path(FAR char *buffer, size_t size, + FAR const char *name); +int pkg_store_format_txn_path(FAR char *buffer, size_t size, + FAR const char *name); +int pkg_store_format_lock_path(FAR char *buffer, size_t size, + FAR const char *name); +int pkg_store_format_download_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version); + +const char *pkg_txn_state_str(enum pkg_txn_state_e state); + +void pkg_error(FAR const char *fmt, ...); +void pkg_info(FAR const char *fmt, ...); + +#endif /* __APPS_SYSTEM_PKG_PKG_H */ diff --git a/system/pkg/pkg_log.c b/system/pkg/pkg_log.c new file mode 100644 index 00000000000..cf6a617fe9b --- /dev/null +++ b/system/pkg/pkg_log.c @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/system/pkg/pkg_log.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "pkg.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void pkg_vlog(FAR FILE *stream, FAR const char *level, + FAR const char *fmt, va_list ap) +{ + fprintf(stream, "pkg: %s: ", level); + vfprintf(stream, fmt, ap); + fputc('\n', stream); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void pkg_error(FAR const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + pkg_vlog(stderr, "error", fmt, ap); + va_end(ap); +} + +void pkg_info(FAR const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + pkg_vlog(stdout, "info", fmt, ap); + va_end(ap); +} diff --git a/system/pkg/pkg_main.c b/system/pkg/pkg_main.c index 3a1eb5dbad4..0774202d47b 100644 --- a/system/pkg/pkg_main.c +++ b/system/pkg/pkg_main.c @@ -29,6 +29,8 @@ #include #include +#include "pkg.h" + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -42,9 +44,7 @@ static void pkg_show_usage(FAR FILE *stream, FAR const char *progname) static int pkg_not_implemented(FAR const char *cmd) { - fprintf(stderr, - "ERROR: 'pkg %s' is not implemented yet in the current unit.\n", - cmd); + pkg_error("'%s' is not implemented yet in the current unit", cmd); return EXIT_FAILURE; } diff --git a/system/pkg/pkg_manifest.c b/system/pkg/pkg_manifest.c new file mode 100644 index 00000000000..7bd6d71f69c --- /dev/null +++ b/system/pkg/pkg_manifest.c @@ -0,0 +1,130 @@ +/**************************************************************************** + * apps/system/pkg/pkg_manifest.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "pkg.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static bool pkg_has_nonspace(FAR const char *value) +{ + while (*value != '\0') + { + if (!isspace((unsigned char)*value)) + { + return true; + } + + value++; + } + + return false; +} + +static int pkg_validate_required(FAR const char *value) +{ + if (value == NULL || value[0] == '\0' || !pkg_has_nonspace(value)) + { + return -EINVAL; + } + + return 0; +} + +static bool pkg_validate_hex(FAR const char *value) +{ + while (*value != '\0') + { + if (!isxdigit((unsigned char)*value)) + { + return false; + } + + value++; + } + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +const char *pkg_manifest_type_str(enum pkg_payload_type_e type) +{ + switch (type) + { + case PKG_PAYLOAD_ELF: + return "elf"; + + case PKG_PAYLOAD_SHARED_LIB: + return "shared-lib"; + + default: + return "unknown"; + } +} + +int pkg_manifest_validate(FAR const struct pkg_manifest_s *manifest) +{ + if (manifest == NULL) + { + return -EINVAL; + } + + if (pkg_validate_required(manifest->name) < 0 || + pkg_validate_required(manifest->version) < 0 || + pkg_validate_required(manifest->arch) < 0 || + pkg_validate_required(manifest->compat) < 0 || + pkg_validate_required(manifest->artifact) < 0 || + pkg_validate_required(manifest->sha256) < 0) + { + return -EINVAL; + } + + if (strlen(manifest->sha256) != PKG_HASH_HEX_LEN) + { + return -EINVAL; + } + + if (!pkg_validate_hex(manifest->sha256)) + { + return -EINVAL; + } + + if (manifest->type != PKG_PAYLOAD_ELF && + manifest->type != PKG_PAYLOAD_SHARED_LIB) + { + return -EINVAL; + } + + return 0; +} diff --git a/system/pkg/pkg_store.c b/system/pkg/pkg_store.c new file mode 100644 index 00000000000..a65b5603f85 --- /dev/null +++ b/system/pkg/pkg_store.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * apps/system/pkg/pkg_store.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "pkg.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int pkg_store_format(FAR char *buffer, size_t size, + FAR const char *fmt, + FAR const char *name, + FAR const char *version) +{ + int ret; + + ret = snprintf(buffer, size, fmt, name, version); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= size) + { + return -ENAMETOOLONG; + } + + return 0; +} + +static int pkg_store_mkdir(FAR const char *path) +{ + struct stat st; + int ret; + + ret = stat(path, &st); + if (ret == 0) + { + return S_ISDIR(st.st_mode) ? 0 : -ENOTDIR; + } + + if (errno != ENOENT) + { + return -errno; + } + + ret = mkdir(path, 0755); + if (ret < 0 && errno != EEXIST) + { + return -errno; + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int pkg_store_prepare_layout(void) +{ + int ret; + + ret = pkg_store_mkdir("/data"); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_mkdir(PKG_REPO_DIR); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_mkdir(PKG_STORE_DIR); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_mkdir(PKG_TMP_DIR); + if (ret < 0) + { + return ret; + } + + return pkg_store_mkdir(PKG_TMP_PKG_DIR); +} + +int pkg_store_format_index_path(FAR char *buffer, size_t size) +{ + return pkg_store_format(buffer, size, "%s", PKG_REPO_INDEX, ""); +} + +int pkg_store_format_installed_path(FAR char *buffer, size_t size) +{ + return pkg_store_format(buffer, size, "%s", PKG_REPO_INSTALLED, ""); +} + +int pkg_store_format_package_root(FAR char *buffer, size_t size, + FAR const char *name) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s", name, ""); +} + +int pkg_store_format_version_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/%s", name, version); +} + +int pkg_store_format_current_path(FAR char *buffer, size_t size, + FAR const char *name) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/current", name, ""); +} + +int pkg_store_format_previous_path(FAR char *buffer, size_t size, + FAR const char *name) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/previous", name, + ""); +} + +int pkg_store_format_txn_path(FAR char *buffer, size_t size, + FAR const char *name) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/.txn", name, ""); +} + +int pkg_store_format_lock_path(FAR char *buffer, size_t size, + FAR const char *name) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/.lock", name, ""); +} + +int pkg_store_format_download_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version) +{ + return pkg_store_format(buffer, size, PKG_TMP_PKG_DIR "/%s-%s.npkg", name, + version); +} diff --git a/system/pkg/pkg_txn.c b/system/pkg/pkg_txn.c new file mode 100644 index 00000000000..72cda801feb --- /dev/null +++ b/system/pkg/pkg_txn.c @@ -0,0 +1,67 @@ +/**************************************************************************** + * apps/system/pkg/pkg_txn.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "pkg.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +const char *pkg_txn_state_str(enum pkg_txn_state_e state) +{ + switch (state) + { + case PKG_TXN_IDLE: + return "IDLE"; + + case PKG_TXN_FETCHING: + return "FETCHING"; + + case PKG_TXN_VERIFIED: + return "VERIFIED"; + + case PKG_TXN_STAGED: + return "STAGED"; + + case PKG_TXN_COMPAT_OK: + return "COMPAT_OK"; + + case PKG_TXN_ACTIVATED: + return "ACTIVATED"; + + case PKG_TXN_CLEANUP: + return "CLEANUP"; + + case PKG_TXN_FAILED: + return "FAILED"; + + case PKG_TXN_RESTORE: + return "RESTORE"; + + default: + return "UNKNOWN"; + } +} From fa22ef6309ddf75fbe79c14701b70bca5731ad7b Mon Sep 17 00:00:00 2001 From: aviralgarg05 Date: Wed, 6 May 2026 21:37:50 +0530 Subject: [PATCH 3/6] system/pkg: add local install and list path --- system/pkg/CMakeLists.txt | 4 + system/pkg/Kconfig | 1 + system/pkg/Makefile | 3 +- system/pkg/pkg.h | 70 ++++ system/pkg/pkg_compat.c | 64 ++++ system/pkg/pkg_hash.c | 313 +++++++++++++++++ system/pkg/pkg_install.c | 462 +++++++++++++++++++++++++ system/pkg/pkg_main.c | 30 +- system/pkg/pkg_manifest.c | 25 ++ system/pkg/pkg_metadata.c | 694 ++++++++++++++++++++++++++++++++++++++ system/pkg/pkg_store.c | 288 ++++++++++++++++ system/pkg/pkg_txn.c | 43 +++ 12 files changed, 1986 insertions(+), 11 deletions(-) create mode 100644 system/pkg/pkg_compat.c create mode 100644 system/pkg/pkg_hash.c create mode 100644 system/pkg/pkg_install.c create mode 100644 system/pkg/pkg_metadata.c diff --git a/system/pkg/CMakeLists.txt b/system/pkg/CMakeLists.txt index 2116efab5e7..075caa2ba24 100644 --- a/system/pkg/CMakeLists.txt +++ b/system/pkg/CMakeLists.txt @@ -32,8 +32,12 @@ if(CONFIG_SYSTEM_PKG) ${CONFIG_SYSTEM_PKG_PRIORITY} SRCS pkg_main.c + pkg_compat.c + pkg_hash.c + pkg_install.c pkg_log.c pkg_manifest.c + pkg_metadata.c pkg_store.c pkg_txn.c) endif() diff --git a/system/pkg/Kconfig b/system/pkg/Kconfig index f73af71d4ba..ce2cf81a8c0 100644 --- a/system/pkg/Kconfig +++ b/system/pkg/Kconfig @@ -6,6 +6,7 @@ config SYSTEM_PKG tristate "'pkg' lifecycle helper" default n + select NETUTILS_CJSON ---help--- Enable the thin package lifecycle helper proposed for the Dynamic ELF distribution work. diff --git a/system/pkg/Makefile b/system/pkg/Makefile index 6020ad557aa..6748f95d5f6 100644 --- a/system/pkg/Makefile +++ b/system/pkg/Makefile @@ -27,7 +27,8 @@ PRIORITY = $(CONFIG_SYSTEM_PKG_PRIORITY) STACKSIZE = $(CONFIG_SYSTEM_PKG_STACKSIZE) MODULE = $(CONFIG_SYSTEM_PKG) -CSRCS = pkg_log.c pkg_manifest.c pkg_store.c pkg_txn.c +CSRCS = pkg_compat.c pkg_hash.c pkg_install.c pkg_log.c pkg_manifest.c +CSRCS += pkg_metadata.c pkg_store.c pkg_txn.c MAINSRC = pkg_main.c include $(APPDIR)/Application.mk diff --git a/system/pkg/pkg.h b/system/pkg/pkg.h index 8ae8758d0cf..181ef23bfe6 100644 --- a/system/pkg/pkg.h +++ b/system/pkg/pkg.h @@ -30,6 +30,7 @@ #include #include #include +#include /**************************************************************************** * Pre-processor Definitions @@ -47,6 +48,9 @@ #define PKG_ARCH_MAX 31 #define PKG_COMPAT_MAX 63 #define PKG_HASH_HEX_LEN 64 +#define PKG_INDEX_MAX 32 +#define PKG_INSTALLED_MAX 16 +#define PKG_INSTALLED_VERSIONS_MAX 8 /**************************************************************************** * Public Types @@ -82,14 +86,43 @@ struct pkg_manifest_s enum pkg_payload_type_e type; }; +struct pkg_index_s +{ + struct pkg_manifest_s manifests[PKG_INDEX_MAX]; + size_t count; +}; + +struct pkg_installed_entry_s +{ + char name[PKG_NAME_MAX + 1]; + char current[PKG_VERSION_MAX + 1]; + char previous[PKG_VERSION_MAX + 1]; + char arch[PKG_ARCH_MAX + 1]; + char compat[PKG_COMPAT_MAX + 1]; + char versions[PKG_INSTALLED_VERSIONS_MAX][PKG_VERSION_MAX + 1]; + enum pkg_payload_type_e type; + size_t version_count; +}; + +struct pkg_installed_db_s +{ + struct pkg_installed_entry_s entries[PKG_INSTALLED_MAX]; + size_t count; +}; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ const char *pkg_manifest_type_str(enum pkg_payload_type_e type); int pkg_manifest_validate(FAR const struct pkg_manifest_s *manifest); +int pkg_manifest_parse_type(FAR const char *value, + FAR enum pkg_payload_type_e *type); int pkg_store_prepare_layout(void); +int pkg_store_ensure_package_root(FAR const char *name); +int pkg_store_ensure_version_dir(FAR const char *name, + FAR const char *version); int pkg_store_format_index_path(FAR char *buffer, size_t size); int pkg_store_format_installed_path(FAR char *buffer, size_t size); int pkg_store_format_package_root(FAR char *buffer, size_t size, @@ -108,8 +141,45 @@ int pkg_store_format_lock_path(FAR char *buffer, size_t size, int pkg_store_format_download_path(FAR char *buffer, size_t size, FAR const char *name, FAR const char *version); +int pkg_store_format_payload_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version, + FAR const char *artifact); +int pkg_store_format_manifest_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version); +int pkg_store_read_text(FAR const char *path, FAR char **buffer); +int pkg_store_write_text_atomic(FAR const char *path, FAR const char *text); +int pkg_store_copy_file(FAR const char *src, FAR const char *dest); +int pkg_store_remove_file(FAR const char *path); + +const char *pkg_runtime_arch(void); +const char *pkg_runtime_compat(void); +int pkg_compat_check(FAR const struct pkg_manifest_s *manifest); + +int pkg_hash_file_sha256(FAR const char *path, + FAR char digest[PKG_HASH_HEX_LEN + 1]); + +int pkg_metadata_load_index(FAR struct pkg_index_s *index); +FAR const struct pkg_manifest_s * +pkg_metadata_find_latest(FAR const struct pkg_index_s *index, + FAR const char *name); +int pkg_metadata_load_installed(FAR struct pkg_installed_db_s *db); +int pkg_metadata_save_installed(FAR const struct pkg_installed_db_s *db); +FAR struct pkg_installed_entry_s * +pkg_metadata_find_installed(FAR struct pkg_installed_db_s *db, + FAR const char *name); +int pkg_metadata_write_manifest(FAR const char *path, + FAR const struct pkg_manifest_s *manifest); +int pkg_metadata_print_installed(FAR FILE *stream, + FAR const struct pkg_installed_db_s *db); const char *pkg_txn_state_str(enum pkg_txn_state_e state); +int pkg_txn_write_state(FAR const char *name, enum pkg_txn_state_e state); +int pkg_txn_clear_state(FAR const char *name); + +int pkg_install(FAR const char *name); +int pkg_list(FAR FILE *stream); void pkg_error(FAR const char *fmt, ...); void pkg_info(FAR const char *fmt, ...); diff --git a/system/pkg/pkg_compat.c b/system/pkg/pkg_compat.c new file mode 100644 index 00000000000..d596459519c --- /dev/null +++ b/system/pkg/pkg_compat.c @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/system/pkg/pkg_compat.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "pkg.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +const char *pkg_runtime_arch(void) +{ + return CONFIG_ARCH; +} + +const char *pkg_runtime_compat(void) +{ + return CONFIG_ARCH_BOARD; +} + +int pkg_compat_check(FAR const struct pkg_manifest_s *manifest) +{ + if (manifest == NULL) + { + return -EINVAL; + } + + if (strcmp(manifest->arch, pkg_runtime_arch()) != 0) + { + return -EXDEV; + } + + if (strcmp(manifest->compat, pkg_runtime_compat()) != 0) + { + return -EXDEV; + } + + return 0; +} diff --git a/system/pkg/pkg_hash.c b/system/pkg/pkg_hash.c new file mode 100644 index 00000000000..e0724594bd9 --- /dev/null +++ b/system/pkg/pkg_hash.c @@ -0,0 +1,313 @@ +/**************************************************************************** + * apps/system/pkg/pkg_hash.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "pkg.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pkg_sha256_s +{ + uint32_t state[8]; + uint64_t bitlen; + uint8_t block[64]; + size_t used; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint32_t g_pkg_sha256_k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t pkg_rotr32(uint32_t value, uint32_t shift) +{ + return (value >> shift) | (value << (32 - shift)); +} + +static uint32_t pkg_load_be32(FAR const uint8_t *buffer) +{ + return ((uint32_t)buffer[0] << 24) | + ((uint32_t)buffer[1] << 16) | + ((uint32_t)buffer[2] << 8) | + (uint32_t)buffer[3]; +} + +static void pkg_store_be32(FAR uint8_t *buffer, uint32_t value) +{ + buffer[0] = (uint8_t)(value >> 24); + buffer[1] = (uint8_t)(value >> 16); + buffer[2] = (uint8_t)(value >> 8); + buffer[3] = (uint8_t)value; +} + +static void pkg_store_be64(FAR uint8_t *buffer, uint64_t value) +{ + int i; + + for (i = 7; i >= 0; i--) + { + buffer[i] = (uint8_t)value; + value >>= 8; + } +} + +static void pkg_sha256_transform(FAR struct pkg_sha256_s *ctx, + FAR const uint8_t *block) +{ + uint32_t w[64]; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t e; + uint32_t f; + uint32_t g; + uint32_t h; + int i; + + for (i = 0; i < 16; i++) + { + w[i] = pkg_load_be32(block + i * 4); + } + + for (i = 16; i < 64; i++) + { + uint32_t s0; + uint32_t s1; + + s0 = pkg_rotr32(w[i - 15], 7) ^ pkg_rotr32(w[i - 15], 18) ^ + (w[i - 15] >> 3); + s1 = pkg_rotr32(w[i - 2], 17) ^ pkg_rotr32(w[i - 2], 19) ^ + (w[i - 2] >> 10); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; i++) + { + uint32_t s1; + uint32_t ch; + uint32_t temp1; + uint32_t s0; + uint32_t maj; + uint32_t temp2; + + s1 = pkg_rotr32(e, 6) ^ pkg_rotr32(e, 11) ^ pkg_rotr32(e, 25); + ch = (e & f) ^ ((~e) & g); + temp1 = h + s1 + ch + g_pkg_sha256_k[i] + w[i]; + s0 = pkg_rotr32(a, 2) ^ pkg_rotr32(a, 13) ^ pkg_rotr32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + temp2 = s0 + maj; + + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +static void pkg_sha256_init(FAR struct pkg_sha256_s *ctx) +{ + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; + ctx->bitlen = 0; + ctx->used = 0; +} + +static void pkg_sha256_update(FAR struct pkg_sha256_s *ctx, + FAR const uint8_t *data, size_t length) +{ + while (length > 0) + { + size_t remaining; + size_t tocopy; + + remaining = sizeof(ctx->block) - ctx->used; + tocopy = length < remaining ? length : remaining; + + memcpy(ctx->block + ctx->used, data, tocopy); + ctx->used += tocopy; + ctx->bitlen += (uint64_t)tocopy * 8; + data += tocopy; + length -= tocopy; + + if (ctx->used == sizeof(ctx->block)) + { + pkg_sha256_transform(ctx, ctx->block); + ctx->used = 0; + } + } +} + +static void pkg_sha256_final(FAR struct pkg_sha256_s *ctx, + FAR uint8_t digest[32]) +{ + size_t i; + + ctx->block[ctx->used++] = 0x80; + + if (ctx->used > 56) + { + while (ctx->used < 64) + { + ctx->block[ctx->used++] = 0; + } + + pkg_sha256_transform(ctx, ctx->block); + ctx->used = 0; + } + + while (ctx->used < 56) + { + ctx->block[ctx->used++] = 0; + } + + pkg_store_be64(ctx->block + 56, ctx->bitlen); + pkg_sha256_transform(ctx, ctx->block); + + for (i = 0; i < 8; i++) + { + pkg_store_be32(digest + i * 4, ctx->state[i]); + } +} + +static void pkg_hex_encode(FAR const uint8_t *input, size_t length, + FAR char *output) +{ + static const char g_hex[] = "0123456789abcdef"; + size_t i; + + for (i = 0; i < length; i++) + { + output[i * 2] = g_hex[input[i] >> 4]; + output[i * 2 + 1] = g_hex[input[i] & 0x0f]; + } + + output[length * 2] = '\0'; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int pkg_hash_file_sha256(FAR const char *path, + FAR char digest[PKG_HASH_HEX_LEN + 1]) +{ + struct pkg_sha256_s ctx; + FAR FILE *stream; + uint8_t raw[32]; + uint8_t buffer[512]; + size_t nread; + + stream = fopen(path, "rb"); + if (stream == NULL) + { + return -errno; + } + + pkg_sha256_init(&ctx); + + for (; ; ) + { + nread = fread(buffer, 1, sizeof(buffer), stream); + if (nread > 0) + { + pkg_sha256_update(&ctx, buffer, nread); + } + + if (nread < sizeof(buffer)) + { + if (ferror(stream)) + { + fclose(stream); + return -EIO; + } + + break; + } + } + + fclose(stream); + pkg_sha256_final(&ctx, raw); + pkg_hex_encode(raw, sizeof(raw), digest); + return 0; +} diff --git a/system/pkg/pkg_install.c b/system/pkg/pkg_install.c new file mode 100644 index 00000000000..0d07ba83fc3 --- /dev/null +++ b/system/pkg/pkg_install.c @@ -0,0 +1,462 @@ +/**************************************************************************** + * apps/system/pkg/pkg_install.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "pkg.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int pkg_install_resolve_artifact(FAR char *buffer, size_t size, + FAR const struct pkg_manifest_s *manifest) +{ + int ret; + + if (manifest->artifact[0] == '/') + { + ret = snprintf(buffer, size, "%s", manifest->artifact); + if (ret < 0) + { + return ret; + } + + return (size_t)ret >= size ? -ENAMETOOLONG : 0; + } + + ret = snprintf(buffer, size, "%s/%s", PKG_REPO_DIR, manifest->artifact); + if (ret < 0) + { + return ret; + } + + return (size_t)ret >= size ? -ENAMETOOLONG : 0; +} + +static int pkg_install_acquire_lock(FAR const char *name, FAR char *path, + size_t size) +{ + int fd; + int ret; + + ret = pkg_store_ensure_package_root(name); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_format_lock_path(path, size, name); + if (ret < 0) + { + return ret; + } + + fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd < 0) + { + return errno == EEXIST ? -EBUSY : -errno; + } + + close(fd); + return 0; +} + +static bool pkg_install_has_version(FAR const struct pkg_installed_entry_s *entry, + FAR const char *version) +{ + size_t i; + + for (i = 0; i < entry->version_count; i++) + { + if (strcmp(entry->versions[i], version) == 0) + { + return true; + } + } + + return false; +} + +static int pkg_install_add_version(FAR struct pkg_installed_entry_s *entry, + FAR const char *version) +{ + int ret; + + if (pkg_install_has_version(entry, version)) + { + return 0; + } + + if (entry->version_count >= PKG_INSTALLED_VERSIONS_MAX) + { + return -E2BIG; + } + + ret = snprintf(entry->versions[entry->version_count], + sizeof(entry->versions[entry->version_count]), "%s", version); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= sizeof(entry->versions[entry->version_count])) + { + return -ENAMETOOLONG; + } + + entry->version_count++; + return 0; +} + +static int pkg_install_update_installed(FAR struct pkg_installed_db_s *db, + FAR const struct pkg_manifest_s *manifest) +{ + FAR struct pkg_installed_entry_s *entry; + int ret; + + entry = pkg_metadata_find_installed(db, manifest->name); + if (entry == NULL) + { + if (db->count >= PKG_INSTALLED_MAX) + { + return -E2BIG; + } + + entry = &db->entries[db->count++]; + memset(entry, 0, sizeof(*entry)); + ret = snprintf(entry->name, sizeof(entry->name), "%s", manifest->name); + if (ret < 0 || (size_t)ret >= sizeof(entry->name)) + { + return -ENAMETOOLONG; + } + } + + if (entry->current[0] != '\0' && + strcmp(entry->current, manifest->version) != 0) + { + ret = snprintf(entry->previous, sizeof(entry->previous), "%s", + entry->current); + if (ret < 0 || (size_t)ret >= sizeof(entry->previous)) + { + return -ENAMETOOLONG; + } + } + + ret = snprintf(entry->current, sizeof(entry->current), "%s", + manifest->version); + if (ret < 0 || (size_t)ret >= sizeof(entry->current)) + { + return -ENAMETOOLONG; + } + + ret = snprintf(entry->arch, sizeof(entry->arch), "%s", manifest->arch); + if (ret < 0 || (size_t)ret >= sizeof(entry->arch)) + { + return -ENAMETOOLONG; + } + + ret = snprintf(entry->compat, sizeof(entry->compat), "%s", manifest->compat); + if (ret < 0 || (size_t)ret >= sizeof(entry->compat)) + { + return -ENAMETOOLONG; + } + + entry->type = manifest->type; + return pkg_install_add_version(entry, manifest->version); +} + +static int pkg_install_write_pointers(FAR const struct pkg_installed_db_s *db, + FAR const struct pkg_manifest_s *manifest) +{ + FAR struct pkg_installed_entry_s *entry; + char current[PATH_MAX]; + char previous[PATH_MAX]; + int ret; + + entry = pkg_metadata_find_installed((FAR struct pkg_installed_db_s *)db, + manifest->name); + if (entry == NULL) + { + return -ENOENT; + } + + ret = pkg_store_format_current_path(current, sizeof(current), manifest->name); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_format_previous_path(previous, sizeof(previous), + manifest->name); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_write_text_atomic(current, entry->current); + if (ret < 0) + { + return ret; + } + + return pkg_store_write_text_atomic(previous, entry->previous); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int pkg_install(FAR const char *name) +{ + struct pkg_index_s index; + 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] = ""; + char digest[PKG_HASH_HEX_LEN + 1]; + int ret; + + ret = pkg_store_prepare_layout(); + if (ret < 0) + { + pkg_error("unable to prepare package layout: %d", ret); + return EXIT_FAILURE; + } + + ret = pkg_metadata_load_index(&index); + if (ret < 0) + { + pkg_error("unable to load local index metadata: %d", ret); + return EXIT_FAILURE; + } + + manifest = pkg_metadata_find_latest(&index, name); + if (manifest == NULL) + { + pkg_error("package '%s' not found in local index", name); + return EXIT_FAILURE; + } + + ret = pkg_install_resolve_artifact(source, sizeof(source), manifest); + if (ret < 0) + { + pkg_error("artifact path for '%s' is too long", name); + return EXIT_FAILURE; + } + + ret = pkg_install_acquire_lock(name, lock, sizeof(lock)); + if (ret < 0) + { + pkg_error("unable to acquire package lock for '%s': %d", name, ret); + return EXIT_FAILURE; + } + + ret = pkg_txn_write_state(name, PKG_TXN_FETCHING); + if (ret < 0) + { + goto errout; + } + + ret = pkg_store_format_download_path(tmp, sizeof(tmp), manifest->name, + manifest->version); + if (ret < 0) + { + goto errout; + } + + ret = pkg_store_copy_file(source, tmp); + if (ret < 0) + { + goto errout; + } + + ret = pkg_hash_file_sha256(tmp, digest); + if (ret < 0) + { + goto errout; + } + + if (strcasecmp(digest, manifest->sha256) != 0) + { + ret = -EILSEQ; + goto errout; + } + + ret = pkg_txn_write_state(name, PKG_TXN_VERIFIED); + if (ret < 0) + { + goto errout; + } + + ret = pkg_store_ensure_version_dir(manifest->name, manifest->version); + if (ret < 0) + { + goto errout; + } + + ret = pkg_store_format_payload_path(payload, sizeof(payload), manifest->name, + manifest->version, manifest->artifact); + if (ret < 0) + { + goto errout; + } + + ret = pkg_store_copy_file(tmp, payload); + if (ret < 0) + { + goto errout; + } + + ret = pkg_store_format_manifest_path(manifest_path, sizeof(manifest_path), + manifest->name, manifest->version); + if (ret < 0) + { + goto errout; + } + + ret = pkg_metadata_write_manifest(manifest_path, manifest); + if (ret < 0) + { + goto errout; + } + + ret = pkg_txn_write_state(name, PKG_TXN_STAGED); + if (ret < 0) + { + goto errout; + } + + ret = pkg_compat_check(manifest); + if (ret < 0) + { + goto errout; + } + + ret = pkg_txn_write_state(name, PKG_TXN_COMPAT_OK); + if (ret < 0) + { + goto errout; + } + + ret = pkg_metadata_load_installed(&installed); + if (ret < 0) + { + goto errout; + } + + ret = pkg_install_update_installed(&installed, manifest); + if (ret < 0) + { + goto errout; + } + + ret = pkg_install_write_pointers(&installed, manifest); + if (ret < 0) + { + goto errout; + } + + ret = pkg_metadata_save_installed(&installed); + if (ret < 0) + { + goto errout; + } + + ret = pkg_txn_write_state(name, PKG_TXN_ACTIVATED); + if (ret < 0) + { + goto errout; + } + + pkg_txn_write_state(name, PKG_TXN_CLEANUP); + if (tmp[0] != '\0') + { + pkg_store_remove_file(tmp); + } + + pkg_txn_clear_state(name); + if (lock[0] != '\0') + { + pkg_store_remove_file(lock); + } + + pkg_info("installed %s version %s", manifest->name, manifest->version); + return EXIT_SUCCESS; + +errout: + pkg_txn_write_state(name, PKG_TXN_FAILED); + if (tmp[0] != '\0') + { + pkg_store_remove_file(tmp); + } + + pkg_txn_clear_state(name); + if (lock[0] != '\0') + { + pkg_store_remove_file(lock); + } + + pkg_error("install failed for '%s': %d", name, ret); + return EXIT_FAILURE; +} + +int pkg_list(FAR FILE *stream) +{ + struct pkg_installed_db_s db; + int ret; + + ret = pkg_store_prepare_layout(); + if (ret < 0) + { + pkg_error("unable to prepare package layout: %d", ret); + return EXIT_FAILURE; + } + + ret = pkg_metadata_load_installed(&db); + if (ret < 0) + { + pkg_error("unable to load installed metadata: %d", ret); + return EXIT_FAILURE; + } + + ret = pkg_metadata_print_installed(stream, &db); + if (ret < 0) + { + pkg_error("unable to print installed metadata: %d", ret); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/system/pkg/pkg_main.c b/system/pkg/pkg_main.c index 0774202d47b..986d47e9dec 100644 --- a/system/pkg/pkg_main.c +++ b/system/pkg/pkg_main.c @@ -42,12 +42,6 @@ static void pkg_show_usage(FAR FILE *stream, FAR const char *progname) progname); } -static int pkg_not_implemented(FAR const char *cmd) -{ - pkg_error("'%s' is not implemented yet in the current unit", cmd); - return EXIT_FAILURE; -} - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -73,22 +67,38 @@ int main(int argc, FAR char *argv[]) if (strcmp(cmd, "install") == 0) { - return pkg_not_implemented("install"); + if (argc != 3) + { + pkg_error("install expects exactly one package name"); + pkg_show_usage(stderr, argv[0]); + return EXIT_FAILURE; + } + + return pkg_install(argv[2]); } if (strcmp(cmd, "update") == 0) { - return pkg_not_implemented("update"); + pkg_error("'update' is not implemented yet in the current unit"); + return EXIT_FAILURE; } if (strcmp(cmd, "list") == 0) { - return pkg_not_implemented("list"); + if (argc != 2) + { + pkg_error("list does not take additional arguments"); + pkg_show_usage(stderr, argv[0]); + return EXIT_FAILURE; + } + + return pkg_list(stdout); } if (strcmp(cmd, "rollback") == 0) { - return pkg_not_implemented("rollback"); + pkg_error("'rollback' is not implemented yet in the current unit"); + return EXIT_FAILURE; } fprintf(stderr, "ERROR: Unknown subcommand '%s'\n", cmd); diff --git a/system/pkg/pkg_manifest.c b/system/pkg/pkg_manifest.c index 7bd6d71f69c..cea425cc7f0 100644 --- a/system/pkg/pkg_manifest.c +++ b/system/pkg/pkg_manifest.c @@ -128,3 +128,28 @@ int pkg_manifest_validate(FAR const struct pkg_manifest_s *manifest) return 0; } + +int pkg_manifest_parse_type(FAR const char *value, + FAR enum pkg_payload_type_e *type) +{ + if (value == NULL || type == NULL) + { + return -EINVAL; + } + + if (strcmp(value, "elf") == 0) + { + *type = PKG_PAYLOAD_ELF; + return 0; + } + + if (strcmp(value, "shared-lib") == 0 || + strcmp(value, "shared_lib") == 0 || + strcmp(value, "shared") == 0) + { + *type = PKG_PAYLOAD_SHARED_LIB; + return 0; + } + + return -EINVAL; +} diff --git a/system/pkg/pkg_metadata.c b/system/pkg/pkg_metadata.c new file mode 100644 index 00000000000..28a28305b6c --- /dev/null +++ b/system/pkg/pkg_metadata.c @@ -0,0 +1,694 @@ +/**************************************************************************** + * apps/system/pkg/pkg_metadata.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "netutils/cJSON.h" + +#include "pkg.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int pkg_copy_string(FAR char *dest, size_t size, FAR const char *src) +{ + int ret; + + ret = snprintf(dest, size, "%s", src); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= size) + { + return -ENAMETOOLONG; + } + + return 0; +} + +static FAR cJSON *pkg_metadata_packages_array(FAR cJSON *root) +{ + if (cJSON_IsArray(root)) + { + return root; + } + + return cJSON_GetObjectItemCaseSensitive(root, "packages"); +} + +static int pkg_metadata_parse_manifest(FAR cJSON *item, + FAR struct pkg_manifest_s *manifest) +{ + FAR cJSON *field; + FAR const char *value; + int ret; + + memset(manifest, 0, sizeof(*manifest)); + + field = cJSON_GetObjectItemCaseSensitive(item, "name"); + value = cJSON_GetStringValue(field); + if (value == NULL || + (ret = pkg_copy_string(manifest->name, sizeof(manifest->name), value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "version"); + value = cJSON_GetStringValue(field); + if (value == NULL || (ret = pkg_copy_string(manifest->version, + sizeof(manifest->version), + value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "arch"); + value = cJSON_GetStringValue(field); + if (value == NULL || + (ret = pkg_copy_string(manifest->arch, sizeof(manifest->arch), value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "compat"); + value = cJSON_GetStringValue(field); + if (value == NULL || (ret = pkg_copy_string(manifest->compat, + sizeof(manifest->compat), + value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "artifact"); + value = cJSON_GetStringValue(field); + if (value == NULL || (ret = pkg_copy_string(manifest->artifact, + sizeof(manifest->artifact), + value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "sha256"); + value = cJSON_GetStringValue(field); + if (value == NULL || + (ret = pkg_copy_string(manifest->sha256, sizeof(manifest->sha256), value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "type"); + value = cJSON_GetStringValue(field); + if (pkg_manifest_parse_type(value, &manifest->type) < 0) + { + return -EINVAL; + } + + return pkg_manifest_validate(manifest); +} + +static int pkg_metadata_parse_versions(FAR cJSON *versions, + FAR struct pkg_installed_entry_s *entry) +{ + FAR cJSON *item; + size_t count = 0; + + if (!cJSON_IsArray(versions)) + { + return -EINVAL; + } + + cJSON_ArrayForEach(item, versions) + { + FAR const char *value; + int ret; + + if (count >= PKG_INSTALLED_VERSIONS_MAX) + { + return -E2BIG; + } + + value = cJSON_GetStringValue(item); + if (value == NULL) + { + return -EINVAL; + } + + ret = pkg_copy_string(entry->versions[count], + sizeof(entry->versions[count]), value); + if (ret < 0) + { + return ret; + } + + count++; + } + + entry->version_count = count; + return 0; +} + +static int pkg_metadata_parse_installed_entry(FAR cJSON *item, + FAR struct pkg_installed_entry_s *entry) +{ + FAR cJSON *field; + FAR const char *value; + int ret; + + memset(entry, 0, sizeof(*entry)); + + field = cJSON_GetObjectItemCaseSensitive(item, "name"); + value = cJSON_GetStringValue(field); + if (value == NULL || + (ret = pkg_copy_string(entry->name, sizeof(entry->name), value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "current"); + value = cJSON_GetStringValue(field); + if (value == NULL || (ret = pkg_copy_string(entry->current, + sizeof(entry->current), + value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "previous"); + value = cJSON_GetStringValue(field); + if (value != NULL) + { + ret = pkg_copy_string(entry->previous, sizeof(entry->previous), value); + if (ret < 0) + { + return ret; + } + } + + field = cJSON_GetObjectItemCaseSensitive(item, "arch"); + value = cJSON_GetStringValue(field); + if (value == NULL || + (ret = pkg_copy_string(entry->arch, sizeof(entry->arch), value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "compat"); + value = cJSON_GetStringValue(field); + if (value == NULL || + (ret = pkg_copy_string(entry->compat, sizeof(entry->compat), value)) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "type"); + value = cJSON_GetStringValue(field); + if (pkg_manifest_parse_type(value, &entry->type) < 0) + { + return -EINVAL; + } + + field = cJSON_GetObjectItemCaseSensitive(item, "versions"); + ret = pkg_metadata_parse_versions(field, entry); + if (ret < 0) + { + return ret; + } + + return 0; +} + +static int pkg_metadata_version_token_cmp(FAR const char *lhs, + FAR const char *rhs) +{ + long leftnum; + long rightnum; + FAR char *leftend; + FAR char *rightend; + + leftnum = strtol(lhs, &leftend, 10); + rightnum = strtol(rhs, &rightend, 10); + + if (leftend != lhs && rightend != rhs) + { + if (leftnum < rightnum) + { + return -1; + } + + if (leftnum > rightnum) + { + return 1; + } + } + else + { + int ret; + + ret = strcmp(lhs, rhs); + if (ret < 0) + { + return -1; + } + + if (ret > 0) + { + return 1; + } + } + + return 0; +} + +static int pkg_metadata_version_cmp(FAR const char *lhs, FAR const char *rhs) +{ + while (*lhs != '\0' || *rhs != '\0') + { + char leftbuf[16]; + char rightbuf[16]; + size_t leftlen = 0; + size_t rightlen = 0; + int ret; + + while (lhs[leftlen] != '\0' && lhs[leftlen] != '.') + { + if (leftlen + 1 >= sizeof(leftbuf)) + { + return strcmp(lhs, rhs); + } + + leftbuf[leftlen] = lhs[leftlen]; + leftlen++; + } + + while (rhs[rightlen] != '\0' && rhs[rightlen] != '.') + { + if (rightlen + 1 >= sizeof(rightbuf)) + { + return strcmp(lhs, rhs); + } + + rightbuf[rightlen] = rhs[rightlen]; + rightlen++; + } + + leftbuf[leftlen] = '\0'; + rightbuf[rightlen] = '\0'; + + ret = pkg_metadata_version_token_cmp(leftbuf, rightbuf); + if (ret != 0) + { + return ret; + } + + lhs += leftlen; + rhs += rightlen; + + if (*lhs == '.') + { + lhs++; + } + + if (*rhs == '.') + { + rhs++; + } + } + + return 0; +} + +static FAR cJSON *pkg_metadata_manifest_to_json(FAR const struct pkg_manifest_s *manifest) +{ + FAR cJSON *root; + + root = cJSON_CreateObject(); + if (root == NULL) + { + return NULL; + } + + cJSON_AddStringToObject(root, "name", manifest->name); + cJSON_AddStringToObject(root, "version", manifest->version); + cJSON_AddStringToObject(root, "arch", manifest->arch); + cJSON_AddStringToObject(root, "compat", manifest->compat); + cJSON_AddStringToObject(root, "artifact", manifest->artifact); + cJSON_AddStringToObject(root, "sha256", manifest->sha256); + cJSON_AddStringToObject(root, "type", pkg_manifest_type_str(manifest->type)); + return root; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int pkg_metadata_load_index(FAR struct pkg_index_s *index) +{ + FAR cJSON *root; + FAR cJSON *packages; + FAR cJSON *item; + FAR char *text; + char path[PATH_MAX]; + size_t count = 0; + int ret; + + if (index == NULL) + { + return -EINVAL; + } + + memset(index, 0, sizeof(*index)); + + ret = pkg_store_format_index_path(path, sizeof(path)); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_read_text(path, &text); + if (ret < 0) + { + return ret; + } + + root = cJSON_Parse(text); + free(text); + if (root == NULL) + { + return -EINVAL; + } + + packages = pkg_metadata_packages_array(root); + if (!cJSON_IsArray(packages)) + { + cJSON_Delete(root); + return -EINVAL; + } + + cJSON_ArrayForEach(item, packages) + { + if (count >= PKG_INDEX_MAX) + { + cJSON_Delete(root); + return -E2BIG; + } + + ret = pkg_metadata_parse_manifest(item, &index->manifests[count]); + if (ret < 0) + { + cJSON_Delete(root); + return ret; + } + + count++; + } + + index->count = count; + cJSON_Delete(root); + return 0; +} + +FAR const struct pkg_manifest_s * +pkg_metadata_find_latest(FAR const struct pkg_index_s *index, + FAR const char *name) +{ + FAR const struct pkg_manifest_s *best = NULL; + size_t i; + + if (index == NULL || name == NULL) + { + return NULL; + } + + for (i = 0; i < index->count; i++) + { + FAR const struct pkg_manifest_s *candidate = &index->manifests[i]; + + if (strcmp(candidate->name, name) != 0) + { + continue; + } + + if (best == NULL || + pkg_metadata_version_cmp(candidate->version, best->version) > 0) + { + best = candidate; + } + } + + return best; +} + +int pkg_metadata_load_installed(FAR struct pkg_installed_db_s *db) +{ + FAR cJSON *root; + FAR cJSON *packages; + FAR cJSON *item; + FAR char *text; + char path[PATH_MAX]; + size_t count = 0; + int ret; + + if (db == NULL) + { + return -EINVAL; + } + + memset(db, 0, sizeof(*db)); + + ret = pkg_store_format_installed_path(path, sizeof(path)); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_read_text(path, &text); + if (ret == -ENOENT) + { + return 0; + } + + if (ret < 0) + { + return ret; + } + + root = cJSON_Parse(text); + free(text); + if (root == NULL) + { + return -EINVAL; + } + + packages = pkg_metadata_packages_array(root); + if (!cJSON_IsArray(packages)) + { + cJSON_Delete(root); + return -EINVAL; + } + + cJSON_ArrayForEach(item, packages) + { + if (count >= PKG_INSTALLED_MAX) + { + cJSON_Delete(root); + return -E2BIG; + } + + ret = pkg_metadata_parse_installed_entry(item, &db->entries[count]); + if (ret < 0) + { + cJSON_Delete(root); + return ret; + } + + count++; + } + + db->count = count; + cJSON_Delete(root); + return 0; +} + +int pkg_metadata_save_installed(FAR const struct pkg_installed_db_s *db) +{ + FAR cJSON *root; + FAR cJSON *packages; + FAR char *text; + char path[PATH_MAX]; + size_t i; + int ret; + + if (db == NULL) + { + return -EINVAL; + } + + root = cJSON_CreateObject(); + if (root == NULL) + { + return -ENOMEM; + } + + packages = cJSON_AddArrayToObject(root, "packages"); + if (packages == NULL) + { + cJSON_Delete(root); + return -ENOMEM; + } + + for (i = 0; i < db->count; i++) + { + FAR const struct pkg_installed_entry_s *entry = &db->entries[i]; + FAR cJSON *item = cJSON_CreateObject(); + FAR cJSON *versions = cJSON_AddArrayToObject(item, "versions"); + size_t j; + + if (item == NULL || versions == NULL) + { + cJSON_Delete(root); + return -ENOMEM; + } + + cJSON_AddStringToObject(item, "name", entry->name); + cJSON_AddStringToObject(item, "current", entry->current); + cJSON_AddStringToObject(item, "previous", entry->previous); + cJSON_AddStringToObject(item, "arch", entry->arch); + cJSON_AddStringToObject(item, "compat", entry->compat); + cJSON_AddStringToObject(item, "type", + pkg_manifest_type_str(entry->type)); + + for (j = 0; j < entry->version_count; j++) + { + cJSON_AddItemToArray(versions, + cJSON_CreateString(entry->versions[j])); + } + + cJSON_AddItemToArray(packages, item); + } + + text = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + if (text == NULL) + { + return -ENOMEM; + } + + ret = pkg_store_format_installed_path(path, sizeof(path)); + if (ret < 0) + { + cJSON_free(text); + return ret; + } + + ret = pkg_store_write_text_atomic(path, text); + cJSON_free(text); + return ret; +} + +FAR struct pkg_installed_entry_s * +pkg_metadata_find_installed(FAR struct pkg_installed_db_s *db, + FAR const char *name) +{ + size_t i; + + if (db == NULL || name == NULL) + { + return NULL; + } + + for (i = 0; i < db->count; i++) + { + if (strcmp(db->entries[i].name, name) == 0) + { + return &db->entries[i]; + } + } + + return NULL; +} + +int pkg_metadata_write_manifest(FAR const char *path, + FAR const struct pkg_manifest_s *manifest) +{ + FAR cJSON *root; + FAR char *text; + int ret; + + root = pkg_metadata_manifest_to_json(manifest); + if (root == NULL) + { + return -ENOMEM; + } + + text = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + if (text == NULL) + { + return -ENOMEM; + } + + ret = pkg_store_write_text_atomic(path, text); + cJSON_free(text); + return ret; +} + +int pkg_metadata_print_installed(FAR FILE *stream, + FAR const struct pkg_installed_db_s *db) +{ + size_t i; + + if (stream == NULL || db == NULL) + { + return -EINVAL; + } + + for (i = 0; i < db->count; i++) + { + FAR const struct pkg_installed_entry_s *entry = &db->entries[i]; + size_t j; + + fprintf(stream, + "%s current=%s previous=%s type=%s arch=%s compat=%s versions=", + entry->name, + entry->current[0] != '\0' ? entry->current : "-", + entry->previous[0] != '\0' ? entry->previous : "-", + pkg_manifest_type_str(entry->type), entry->arch, entry->compat); + + for (j = 0; j < entry->version_count; j++) + { + fprintf(stream, "%s%s", j == 0 ? "" : ",", entry->versions[j]); + } + + fputc('\n', stream); + } + + return 0; +} diff --git a/system/pkg/pkg_store.c b/system/pkg/pkg_store.c index a65b5603f85..9464ec18aa1 100644 --- a/system/pkg/pkg_store.c +++ b/system/pkg/pkg_store.c @@ -25,9 +25,13 @@ ****************************************************************************/ #include +#include +#include #include #include +#include #include +#include #include "pkg.h" @@ -81,6 +85,39 @@ static int pkg_store_mkdir(FAR const char *path) return 0; } +static int pkg_store_write_all(int fd, FAR const char *buffer, size_t length) +{ + size_t offset = 0; + + while (offset < length) + { + ssize_t ret; + + ret = write(fd, buffer + offset, length - offset); + if (ret < 0) + { + if (errno == EINTR) + { + continue; + } + + return -errno; + } + + offset += (size_t)ret; + } + + return 0; +} + +static FAR const char *pkg_store_basename(FAR const char *path) +{ + FAR const char *leaf; + + leaf = strrchr(path, '/'); + return leaf != NULL ? leaf + 1 : path; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -116,6 +153,41 @@ int pkg_store_prepare_layout(void) return pkg_store_mkdir(PKG_TMP_PKG_DIR); } +int pkg_store_ensure_package_root(FAR const char *name) +{ + char path[PATH_MAX]; + int ret; + + ret = pkg_store_format_package_root(path, sizeof(path), name); + if (ret < 0) + { + return ret; + } + + return pkg_store_mkdir(path); +} + +int pkg_store_ensure_version_dir(FAR const char *name, + FAR const char *version) +{ + char path[PATH_MAX]; + int ret; + + ret = pkg_store_ensure_package_root(name); + if (ret < 0) + { + return ret; + } + + ret = pkg_store_format_version_path(path, sizeof(path), name, version); + if (ret < 0) + { + return ret; + } + + return pkg_store_mkdir(path); +} + int pkg_store_format_index_path(FAR char *buffer, size_t size) { return pkg_store_format(buffer, size, "%s", PKG_REPO_INDEX, ""); @@ -171,3 +243,219 @@ int pkg_store_format_download_path(FAR char *buffer, size_t size, return pkg_store_format(buffer, size, PKG_TMP_PKG_DIR "/%s-%s.npkg", name, version); } + +int pkg_store_format_payload_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version, + FAR const char *artifact) +{ + FAR const char *leaf; + int ret; + + leaf = pkg_store_basename(artifact); + ret = snprintf(buffer, size, PKG_STORE_DIR "/%s/%s/%s", name, version, leaf); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= size) + { + return -ENAMETOOLONG; + } + + return 0; +} + +int pkg_store_format_manifest_path(FAR char *buffer, size_t size, + FAR const char *name, + FAR const char *version) +{ + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/%s/manifest.json", + name, version); +} + +int pkg_store_read_text(FAR const char *path, FAR char **buffer) +{ + FAR FILE *stream; + FAR char *data; + long length; + size_t nread; + + if (buffer == NULL) + { + return -EINVAL; + } + + *buffer = NULL; + + stream = fopen(path, "rb"); + if (stream == NULL) + { + return errno == ENOENT ? -ENOENT : -errno; + } + + if (fseek(stream, 0, SEEK_END) < 0) + { + fclose(stream); + return -errno; + } + + length = ftell(stream); + if (length < 0) + { + fclose(stream); + return -errno; + } + + if (fseek(stream, 0, SEEK_SET) < 0) + { + fclose(stream); + return -errno; + } + + data = malloc((size_t)length + 1); + if (data == NULL) + { + fclose(stream); + return -ENOMEM; + } + + nread = fread(data, 1, (size_t)length, stream); + if (nread != (size_t)length) + { + int err = ferror(stream); + + fclose(stream); + free(data); + return err ? -EIO : -EINVAL; + } + + fclose(stream); + + data[length] = '\0'; + *buffer = data; + return 0; +} + +int pkg_store_write_text_atomic(FAR const char *path, FAR const char *text) +{ + char tmp[PATH_MAX]; + int fd; + int ret; + + ret = snprintf(tmp, sizeof(tmp), "%s.tmp", path); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= sizeof(tmp)) + { + return -ENAMETOOLONG; + } + + fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + return -errno; + } + + ret = pkg_store_write_all(fd, text, strlen(text)); + if (ret < 0) + { + close(fd); + unlink(tmp); + return ret; + } + + if (close(fd) < 0) + { + unlink(tmp); + return -errno; + } + + if (rename(tmp, path) < 0) + { + unlink(tmp); + return -errno; + } + + return 0; +} + +int pkg_store_copy_file(FAR const char *src, FAR const char *dest) +{ + int infd; + int outfd; + int ret; + char buffer[512]; + + infd = open(src, O_RDONLY); + if (infd < 0) + { + return -errno; + } + + outfd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfd < 0) + { + ret = -errno; + close(infd); + return ret; + } + + for (; ; ) + { + ssize_t nread; + + nread = read(infd, buffer, sizeof(buffer)); + if (nread < 0) + { + if (errno == EINTR) + { + continue; + } + + ret = -errno; + goto errout; + } + + if (nread == 0) + { + break; + } + + ret = pkg_store_write_all(outfd, buffer, (size_t)nread); + if (ret < 0) + { + goto errout; + } + } + + close(infd); + + if (close(outfd) < 0) + { + unlink(dest); + return -errno; + } + + return 0; + +errout: + close(infd); + close(outfd); + unlink(dest); + return ret; +} + +int pkg_store_remove_file(FAR const char *path) +{ + if (unlink(path) < 0) + { + return errno == ENOENT ? 0 : -errno; + } + + return 0; +} diff --git a/system/pkg/pkg_txn.c b/system/pkg/pkg_txn.c index 72cda801feb..85b8945c577 100644 --- a/system/pkg/pkg_txn.c +++ b/system/pkg/pkg_txn.c @@ -24,6 +24,9 @@ * Included Files ****************************************************************************/ +#include +#include + #include "pkg.h" /**************************************************************************** @@ -65,3 +68,43 @@ const char *pkg_txn_state_str(enum pkg_txn_state_e state) return "UNKNOWN"; } } + +int pkg_txn_write_state(FAR const char *name, enum pkg_txn_state_e state) +{ + char path[PATH_MAX]; + char text[32]; + int ret; + + ret = pkg_store_format_txn_path(path, sizeof(path), name); + if (ret < 0) + { + return ret; + } + + ret = snprintf(text, sizeof(text), "%s\n", pkg_txn_state_str(state)); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= sizeof(text)) + { + return -ENAMETOOLONG; + } + + return pkg_store_write_text_atomic(path, text); +} + +int pkg_txn_clear_state(FAR const char *name) +{ + char path[PATH_MAX]; + int ret; + + ret = pkg_store_format_txn_path(path, sizeof(path), name); + if (ret < 0) + { + return ret; + } + + return pkg_store_remove_file(path); +} From e0d6b58f433b654c4da690437868bfeb580b4219 Mon Sep 17 00:00:00 2001 From: aviralgarg05 Date: Mon, 11 May 2026 12:06:01 +0530 Subject: [PATCH 4/6] system/nxpkg: rename package helper and keep runtime fixes --- system/{pkg => nxpkg}/CMakeLists.txt | 12 ++--- system/{pkg => nxpkg}/Kconfig | 22 ++++---- system/{pkg => nxpkg}/Make.defs | 6 +-- system/{pkg => nxpkg}/Makefile | 10 ++-- system/{pkg => nxpkg}/pkg.h | 8 +-- system/{pkg => nxpkg}/pkg_compat.c | 2 +- system/{pkg => nxpkg}/pkg_hash.c | 2 +- system/{pkg => nxpkg}/pkg_install.c | 75 +++++++++++++++++++++++----- system/{pkg => nxpkg}/pkg_log.c | 5 +- system/{pkg => nxpkg}/pkg_main.c | 2 +- system/{pkg => nxpkg}/pkg_manifest.c | 2 +- system/{pkg => nxpkg}/pkg_metadata.c | 12 ++++- system/{pkg => nxpkg}/pkg_store.c | 2 +- system/{pkg => nxpkg}/pkg_txn.c | 2 +- 14 files changed, 112 insertions(+), 50 deletions(-) rename system/{pkg => nxpkg}/CMakeLists.txt (86%) rename system/{pkg => nxpkg}/Kconfig (58%) rename system/{pkg => nxpkg}/Make.defs (90%) rename system/{pkg => nxpkg}/Makefile (85%) rename system/{pkg => nxpkg}/pkg.h (98%) rename system/{pkg => nxpkg}/pkg_compat.c (98%) rename system/{pkg => nxpkg}/pkg_hash.c (99%) rename system/{pkg => nxpkg}/pkg_install.c (86%) rename system/{pkg => nxpkg}/pkg_log.c (95%) rename system/{pkg => nxpkg}/pkg_main.c (98%) rename system/{pkg => nxpkg}/pkg_manifest.c (99%) rename system/{pkg => nxpkg}/pkg_metadata.c (97%) rename system/{pkg => nxpkg}/pkg_store.c (99%) rename system/{pkg => nxpkg}/pkg_txn.c (98%) diff --git a/system/pkg/CMakeLists.txt b/system/nxpkg/CMakeLists.txt similarity index 86% rename from system/pkg/CMakeLists.txt rename to system/nxpkg/CMakeLists.txt index 075caa2ba24..8ac3e358532 100644 --- a/system/pkg/CMakeLists.txt +++ b/system/nxpkg/CMakeLists.txt @@ -1,5 +1,5 @@ # ############################################################################## -# apps/system/pkg/CMakeLists.txt +# apps/system/nxpkg/CMakeLists.txt # # SPDX-License-Identifier: Apache-2.0 # @@ -20,16 +20,16 @@ # # ############################################################################## -if(CONFIG_SYSTEM_PKG) +if(CONFIG_SYSTEM_NXPKG) nuttx_add_application( MODULE - ${CONFIG_SYSTEM_PKG} + ${CONFIG_SYSTEM_NXPKG} NAME - ${CONFIG_SYSTEM_PKG_PROGNAME} + ${CONFIG_SYSTEM_NXPKG_PROGNAME} STACKSIZE - ${CONFIG_SYSTEM_PKG_STACKSIZE} + ${CONFIG_SYSTEM_NXPKG_STACKSIZE} PRIORITY - ${CONFIG_SYSTEM_PKG_PRIORITY} + ${CONFIG_SYSTEM_NXPKG_PRIORITY} SRCS pkg_main.c pkg_compat.c diff --git a/system/pkg/Kconfig b/system/nxpkg/Kconfig similarity index 58% rename from system/pkg/Kconfig rename to system/nxpkg/Kconfig index ce2cf81a8c0..49bc9dc3596 100644 --- a/system/pkg/Kconfig +++ b/system/nxpkg/Kconfig @@ -3,29 +3,29 @@ # see the file kconfig-language.txt in the NuttX tools repository. # -config SYSTEM_PKG - tristate "'pkg' lifecycle helper" +config SYSTEM_NXPKG + tristate "'nxpkg' lifecycle helper" default n select NETUTILS_CJSON ---help--- Enable the thin package lifecycle helper proposed for the Dynamic ELF distribution work. -if SYSTEM_PKG +if SYSTEM_NXPKG -config SYSTEM_PKG_PROGNAME +config SYSTEM_NXPKG_PROGNAME string "Program name" - default "pkg" + default "nxpkg" ---help--- This is the name of the program that will be used when the - pkg tool is installed. + nxpkg tool is installed. -config SYSTEM_PKG_PRIORITY - int "'pkg' task priority" +config SYSTEM_NXPKG_PRIORITY + int "'nxpkg' task priority" default 100 -config SYSTEM_PKG_STACKSIZE - int "'pkg' stack size" - default DEFAULT_TASK_STACKSIZE +config SYSTEM_NXPKG_STACKSIZE + int "'nxpkg' stack size" + default 16384 endif diff --git a/system/pkg/Make.defs b/system/nxpkg/Make.defs similarity index 90% rename from system/pkg/Make.defs rename to system/nxpkg/Make.defs index a69b3c46283..bdb799080fe 100644 --- a/system/pkg/Make.defs +++ b/system/nxpkg/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/system/pkg/Make.defs +# apps/system/nxpkg/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -20,6 +20,6 @@ # ############################################################################ -ifneq ($(CONFIG_SYSTEM_PKG),) -CONFIGURED_APPS += $(APPDIR)/system/pkg +ifneq ($(CONFIG_SYSTEM_NXPKG),) +CONFIGURED_APPS += $(APPDIR)/system/nxpkg endif diff --git a/system/pkg/Makefile b/system/nxpkg/Makefile similarity index 85% rename from system/pkg/Makefile rename to system/nxpkg/Makefile index 6748f95d5f6..757f9fc896e 100644 --- a/system/pkg/Makefile +++ b/system/nxpkg/Makefile @@ -1,5 +1,5 @@ ############################################################################ -# apps/system/pkg/Makefile +# apps/system/nxpkg/Makefile # # SPDX-License-Identifier: Apache-2.0 # @@ -22,10 +22,10 @@ include $(APPDIR)/Make.defs -PROGNAME = $(CONFIG_SYSTEM_PKG_PROGNAME) -PRIORITY = $(CONFIG_SYSTEM_PKG_PRIORITY) -STACKSIZE = $(CONFIG_SYSTEM_PKG_STACKSIZE) -MODULE = $(CONFIG_SYSTEM_PKG) +PROGNAME = $(CONFIG_SYSTEM_NXPKG_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_NXPKG_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_NXPKG_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_NXPKG) CSRCS = pkg_compat.c pkg_hash.c pkg_install.c pkg_log.c pkg_manifest.c CSRCS += pkg_metadata.c pkg_store.c pkg_txn.c diff --git a/system/pkg/pkg.h b/system/nxpkg/pkg.h similarity index 98% rename from system/pkg/pkg.h rename to system/nxpkg/pkg.h index 181ef23bfe6..fb5c79cd464 100644 --- a/system/pkg/pkg.h +++ b/system/nxpkg/pkg.h @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg.h + * apps/system/nxpkg/pkg.h * * SPDX-License-Identifier: Apache-2.0 * @@ -20,8 +20,8 @@ * ****************************************************************************/ -#ifndef __APPS_SYSTEM_PKG_PKG_H -#define __APPS_SYSTEM_PKG_PKG_H +#ifndef __APPS_SYSTEM_NXPKG_PKG_H +#define __APPS_SYSTEM_NXPKG_PKG_H /**************************************************************************** * Included Files @@ -184,4 +184,4 @@ int pkg_list(FAR FILE *stream); void pkg_error(FAR const char *fmt, ...); void pkg_info(FAR const char *fmt, ...); -#endif /* __APPS_SYSTEM_PKG_PKG_H */ +#endif /* __APPS_SYSTEM_NXPKG_PKG_H */ diff --git a/system/pkg/pkg_compat.c b/system/nxpkg/pkg_compat.c similarity index 98% rename from system/pkg/pkg_compat.c rename to system/nxpkg/pkg_compat.c index d596459519c..d093cd2b793 100644 --- a/system/pkg/pkg_compat.c +++ b/system/nxpkg/pkg_compat.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_compat.c + * apps/system/nxpkg/pkg_compat.c * * SPDX-License-Identifier: Apache-2.0 * diff --git a/system/pkg/pkg_hash.c b/system/nxpkg/pkg_hash.c similarity index 99% rename from system/pkg/pkg_hash.c rename to system/nxpkg/pkg_hash.c index e0724594bd9..40965067df5 100644 --- a/system/pkg/pkg_hash.c +++ b/system/nxpkg/pkg_hash.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_hash.c + * apps/system/nxpkg/pkg_hash.c * * SPDX-License-Identifier: Apache-2.0 * diff --git a/system/pkg/pkg_install.c b/system/nxpkg/pkg_install.c similarity index 86% rename from system/pkg/pkg_install.c rename to system/nxpkg/pkg_install.c index 0d07ba83fc3..542198aa685 100644 --- a/system/pkg/pkg_install.c +++ b/system/nxpkg/pkg_install.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_install.c + * apps/system/nxpkg/pkg_install.c * * SPDX-License-Identifier: Apache-2.0 * @@ -237,8 +237,8 @@ static int pkg_install_write_pointers(FAR const struct pkg_installed_db_s *db, int pkg_install(FAR const char *name) { - struct pkg_index_s index; - struct pkg_installed_db_s installed; + 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] = ""; @@ -248,27 +248,47 @@ int pkg_install(FAR const char *name) char digest[PKG_HASH_HEX_LEN + 1]; int ret; + index = malloc(sizeof(*index)); + installed = malloc(sizeof(*installed)); + if (index == NULL || installed == NULL) + { + free(index); + free(installed); + pkg_error("unable to allocate package metadata buffers"); + return EXIT_FAILURE; + } + ret = pkg_store_prepare_layout(); if (ret < 0) { + free(index); + free(installed); pkg_error("unable to prepare package layout: %d", ret); return EXIT_FAILURE; } - ret = pkg_metadata_load_index(&index); + pkg_info("layout prepared"); + + 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; } - manifest = pkg_metadata_find_latest(&index, name); + 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; } + pkg_info("selected %s version %s", manifest->name, manifest->version); + ret = pkg_install_resolve_artifact(source, sizeof(source), manifest); if (ret < 0) { @@ -276,6 +296,8 @@ int pkg_install(FAR const char *name) return EXIT_FAILURE; } + pkg_info("artifact source %s", source); + ret = pkg_install_acquire_lock(name, lock, sizeof(lock)); if (ret < 0) { @@ -302,18 +324,24 @@ int pkg_install(FAR const char *name) goto errout; } + pkg_info("artifact copied to staging"); + ret = pkg_hash_file_sha256(tmp, digest); if (ret < 0) { goto errout; } + pkg_info("sha256 computed: %s", digest); + if (strcasecmp(digest, manifest->sha256) != 0) { ret = -EILSEQ; goto errout; } + pkg_info("sha256 verified"); + ret = pkg_txn_write_state(name, PKG_TXN_VERIFIED); if (ret < 0) { @@ -339,6 +367,8 @@ int pkg_install(FAR const char *name) goto errout; } + pkg_info("payload staged at %s", payload); + ret = pkg_store_format_manifest_path(manifest_path, sizeof(manifest_path), manifest->name, manifest->version); if (ret < 0) @@ -352,6 +382,8 @@ int pkg_install(FAR const char *name) goto errout; } + pkg_info("manifest written"); + ret = pkg_txn_write_state(name, PKG_TXN_STAGED); if (ret < 0) { @@ -364,36 +396,40 @@ int pkg_install(FAR const char *name) goto errout; } + pkg_info("compatibility check passed"); + ret = pkg_txn_write_state(name, PKG_TXN_COMPAT_OK); if (ret < 0) { goto errout; } - ret = pkg_metadata_load_installed(&installed); + ret = pkg_metadata_load_installed(installed); if (ret < 0) { goto errout; } - ret = pkg_install_update_installed(&installed, manifest); + ret = pkg_install_update_installed(installed, manifest); if (ret < 0) { goto errout; } - ret = pkg_install_write_pointers(&installed, manifest); + ret = pkg_install_write_pointers(installed, manifest); if (ret < 0) { goto errout; } - ret = pkg_metadata_save_installed(&installed); + ret = pkg_metadata_save_installed(installed); if (ret < 0) { goto errout; } + pkg_info("installed metadata updated"); + ret = pkg_txn_write_state(name, PKG_TXN_ACTIVATED); if (ret < 0) { @@ -413,6 +449,8 @@ int pkg_install(FAR const char *name) } pkg_info("installed %s version %s", manifest->name, manifest->version); + free(index); + free(installed); return EXIT_SUCCESS; errout: @@ -428,35 +466,48 @@ int pkg_install(FAR const char *name) pkg_store_remove_file(lock); } + free(index); + free(installed); pkg_error("install failed for '%s': %d", name, ret); return EXIT_FAILURE; } int pkg_list(FAR FILE *stream) { - struct pkg_installed_db_s db; + FAR struct pkg_installed_db_s *db; int ret; + db = malloc(sizeof(*db)); + if (db == NULL) + { + pkg_error("unable to allocate installed metadata buffer"); + return EXIT_FAILURE; + } + ret = pkg_store_prepare_layout(); if (ret < 0) { + free(db); pkg_error("unable to prepare package layout: %d", ret); return EXIT_FAILURE; } - ret = pkg_metadata_load_installed(&db); + ret = pkg_metadata_load_installed(db); if (ret < 0) { + free(db); pkg_error("unable to load installed metadata: %d", ret); return EXIT_FAILURE; } - ret = pkg_metadata_print_installed(stream, &db); + ret = pkg_metadata_print_installed(stream, db); if (ret < 0) { + free(db); pkg_error("unable to print installed metadata: %d", ret); return EXIT_FAILURE; } + free(db); return EXIT_SUCCESS; } diff --git a/system/pkg/pkg_log.c b/system/nxpkg/pkg_log.c similarity index 95% rename from system/pkg/pkg_log.c rename to system/nxpkg/pkg_log.c index cf6a617fe9b..bed06b13ffb 100644 --- a/system/pkg/pkg_log.c +++ b/system/nxpkg/pkg_log.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_log.c + * apps/system/nxpkg/pkg_log.c * * SPDX-License-Identifier: Apache-2.0 * @@ -36,9 +36,10 @@ static void pkg_vlog(FAR FILE *stream, FAR const char *level, FAR const char *fmt, va_list ap) { - fprintf(stream, "pkg: %s: ", level); + fprintf(stream, "nxpkg: %s: ", level); vfprintf(stream, fmt, ap); fputc('\n', stream); + fflush(stream); } /**************************************************************************** diff --git a/system/pkg/pkg_main.c b/system/nxpkg/pkg_main.c similarity index 98% rename from system/pkg/pkg_main.c rename to system/nxpkg/pkg_main.c index 986d47e9dec..89f3b483357 100644 --- a/system/pkg/pkg_main.c +++ b/system/nxpkg/pkg_main.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_main.c + * apps/system/nxpkg/pkg_main.c * * SPDX-License-Identifier: Apache-2.0 * diff --git a/system/pkg/pkg_manifest.c b/system/nxpkg/pkg_manifest.c similarity index 99% rename from system/pkg/pkg_manifest.c rename to system/nxpkg/pkg_manifest.c index cea425cc7f0..3e7b56a2637 100644 --- a/system/pkg/pkg_manifest.c +++ b/system/nxpkg/pkg_manifest.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_manifest.c + * apps/system/nxpkg/pkg_manifest.c * * SPDX-License-Identifier: Apache-2.0 * diff --git a/system/pkg/pkg_metadata.c b/system/nxpkg/pkg_metadata.c similarity index 97% rename from system/pkg/pkg_metadata.c rename to system/nxpkg/pkg_metadata.c index 28a28305b6c..0859b71afad 100644 --- a/system/pkg/pkg_metadata.c +++ b/system/nxpkg/pkg_metadata.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_metadata.c + * apps/system/nxpkg/pkg_metadata.c * * SPDX-License-Identifier: Apache-2.0 * @@ -377,6 +377,7 @@ int pkg_metadata_load_index(FAR struct pkg_index_s *index) FAR char *text; char path[PATH_MAX]; size_t count = 0; + size_t textlen; int ret; if (index == NULL) @@ -392,13 +393,19 @@ int pkg_metadata_load_index(FAR struct pkg_index_s *index) return ret; } + pkg_info("loading index from %s", path); + ret = pkg_store_read_text(path, &text); if (ret < 0) { return ret; } + textlen = strlen(text); + pkg_info("index read complete (%zu bytes)", textlen); + root = cJSON_Parse(text); + pkg_info("cJSON_Parse returned %s", root != NULL ? "success" : "failure"); free(text); if (root == NULL) { @@ -427,6 +434,9 @@ int pkg_metadata_load_index(FAR struct pkg_index_s *index) return ret; } + pkg_info("parsed manifest %s %s", + index->manifests[count].name, + index->manifests[count].version); count++; } diff --git a/system/pkg/pkg_store.c b/system/nxpkg/pkg_store.c similarity index 99% rename from system/pkg/pkg_store.c rename to system/nxpkg/pkg_store.c index 9464ec18aa1..cacfeec2cc5 100644 --- a/system/pkg/pkg_store.c +++ b/system/nxpkg/pkg_store.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_store.c + * apps/system/nxpkg/pkg_store.c * * SPDX-License-Identifier: Apache-2.0 * diff --git a/system/pkg/pkg_txn.c b/system/nxpkg/pkg_txn.c similarity index 98% rename from system/pkg/pkg_txn.c rename to system/nxpkg/pkg_txn.c index 85b8945c577..e20a8700f44 100644 --- a/system/pkg/pkg_txn.c +++ b/system/nxpkg/pkg_txn.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/system/pkg/pkg_txn.c + * apps/system/nxpkg/pkg_txn.c * * SPDX-License-Identifier: Apache-2.0 * From 7ccf43e29fae09ca30807e2920ad2b014861ab5a Mon Sep 17 00:00:00 2001 From: aviralgarg05 Date: Mon, 11 May 2026 13:38:26 +0530 Subject: [PATCH 5/6] system/nxpkg: drop temporary debug logs and simplify usage --- system/nxpkg/pkg_install.c | 20 -------------------- system/nxpkg/pkg_main.c | 31 +++++++++++++++---------------- system/nxpkg/pkg_metadata.c | 10 ---------- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/system/nxpkg/pkg_install.c b/system/nxpkg/pkg_install.c index 542198aa685..12338b068d3 100644 --- a/system/nxpkg/pkg_install.c +++ b/system/nxpkg/pkg_install.c @@ -267,8 +267,6 @@ int pkg_install(FAR const char *name) return EXIT_FAILURE; } - pkg_info("layout prepared"); - ret = pkg_metadata_load_index(index); if (ret < 0) { @@ -287,8 +285,6 @@ int pkg_install(FAR const char *name) return EXIT_FAILURE; } - pkg_info("selected %s version %s", manifest->name, manifest->version); - ret = pkg_install_resolve_artifact(source, sizeof(source), manifest); if (ret < 0) { @@ -296,8 +292,6 @@ int pkg_install(FAR const char *name) return EXIT_FAILURE; } - pkg_info("artifact source %s", source); - ret = pkg_install_acquire_lock(name, lock, sizeof(lock)); if (ret < 0) { @@ -324,24 +318,18 @@ int pkg_install(FAR const char *name) goto errout; } - pkg_info("artifact copied to staging"); - ret = pkg_hash_file_sha256(tmp, digest); if (ret < 0) { goto errout; } - pkg_info("sha256 computed: %s", digest); - if (strcasecmp(digest, manifest->sha256) != 0) { ret = -EILSEQ; goto errout; } - pkg_info("sha256 verified"); - ret = pkg_txn_write_state(name, PKG_TXN_VERIFIED); if (ret < 0) { @@ -367,8 +355,6 @@ int pkg_install(FAR const char *name) goto errout; } - pkg_info("payload staged at %s", payload); - ret = pkg_store_format_manifest_path(manifest_path, sizeof(manifest_path), manifest->name, manifest->version); if (ret < 0) @@ -382,8 +368,6 @@ int pkg_install(FAR const char *name) goto errout; } - pkg_info("manifest written"); - ret = pkg_txn_write_state(name, PKG_TXN_STAGED); if (ret < 0) { @@ -396,8 +380,6 @@ int pkg_install(FAR const char *name) goto errout; } - pkg_info("compatibility check passed"); - ret = pkg_txn_write_state(name, PKG_TXN_COMPAT_OK); if (ret < 0) { @@ -428,8 +410,6 @@ int pkg_install(FAR const char *name) goto errout; } - pkg_info("installed metadata updated"); - ret = pkg_txn_write_state(name, PKG_TXN_ACTIVATED); if (ret < 0) { diff --git a/system/nxpkg/pkg_main.c b/system/nxpkg/pkg_main.c index 89f3b483357..c049592c5b6 100644 --- a/system/nxpkg/pkg_main.c +++ b/system/nxpkg/pkg_main.c @@ -31,17 +31,6 @@ #include "pkg.h" -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -static void pkg_show_usage(FAR FILE *stream, FAR const char *progname) -{ - fprintf(stream, - "Usage: %s [args]\n", - progname); -} - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -52,7 +41,9 @@ int main(int argc, FAR char *argv[]) if (argc < 2) { - pkg_show_usage(stderr, argv[0]); + fprintf(stderr, + "Usage: %s [args]\n", + argv[0]); return EXIT_FAILURE; } @@ -61,7 +52,9 @@ int main(int argc, FAR char *argv[]) if (strcmp(cmd, "help") == 0 || strcmp(cmd, "--help") == 0 || strcmp(cmd, "-h") == 0) { - pkg_show_usage(stdout, argv[0]); + fprintf(stdout, + "Usage: %s [args]\n", + argv[0]); return EXIT_SUCCESS; } @@ -70,7 +63,9 @@ int main(int argc, FAR char *argv[]) if (argc != 3) { pkg_error("install expects exactly one package name"); - pkg_show_usage(stderr, argv[0]); + fprintf(stderr, + "Usage: %s [args]\n", + argv[0]); return EXIT_FAILURE; } @@ -88,7 +83,9 @@ int main(int argc, FAR char *argv[]) if (argc != 2) { pkg_error("list does not take additional arguments"); - pkg_show_usage(stderr, argv[0]); + fprintf(stderr, + "Usage: %s [args]\n", + argv[0]); return EXIT_FAILURE; } @@ -102,6 +99,8 @@ int main(int argc, FAR char *argv[]) } fprintf(stderr, "ERROR: Unknown subcommand '%s'\n", cmd); - pkg_show_usage(stderr, argv[0]); + fprintf(stderr, + "Usage: %s [args]\n", + argv[0]); return EXIT_FAILURE; } diff --git a/system/nxpkg/pkg_metadata.c b/system/nxpkg/pkg_metadata.c index 0859b71afad..d79d290b6a7 100644 --- a/system/nxpkg/pkg_metadata.c +++ b/system/nxpkg/pkg_metadata.c @@ -377,7 +377,6 @@ int pkg_metadata_load_index(FAR struct pkg_index_s *index) FAR char *text; char path[PATH_MAX]; size_t count = 0; - size_t textlen; int ret; if (index == NULL) @@ -393,19 +392,13 @@ int pkg_metadata_load_index(FAR struct pkg_index_s *index) return ret; } - pkg_info("loading index from %s", path); - ret = pkg_store_read_text(path, &text); if (ret < 0) { return ret; } - textlen = strlen(text); - pkg_info("index read complete (%zu bytes)", textlen); - root = cJSON_Parse(text); - pkg_info("cJSON_Parse returned %s", root != NULL ? "success" : "failure"); free(text); if (root == NULL) { @@ -434,9 +427,6 @@ int pkg_metadata_load_index(FAR struct pkg_index_s *index) return ret; } - pkg_info("parsed manifest %s %s", - index->manifests[count].name, - index->manifests[count].version); count++; } From 901096eaca0ee2cc240695c2c60c5ac41c42e414 Mon Sep 17 00:00:00 2001 From: aviralgarg05 Date: Wed, 13 May 2026 20:59:34 +0530 Subject: [PATCH 6/6] system/nxpkg: use /etc and /var package layout --- system/nxpkg/pkg.h | 12 ++++---- system/nxpkg/pkg_compat.c | 2 +- system/nxpkg/pkg_store.c | 61 ++++++++++++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/system/nxpkg/pkg.h b/system/nxpkg/pkg.h index fb5c79cd464..39a4bf0c21d 100644 --- a/system/nxpkg/pkg.h +++ b/system/nxpkg/pkg.h @@ -36,12 +36,12 @@ * Pre-processor Definitions ****************************************************************************/ -#define PKG_REPO_DIR "/data/repo" -#define PKG_REPO_INDEX "/data/repo/index.json" -#define PKG_REPO_INSTALLED "/data/repo/installed.json" -#define PKG_STORE_DIR "/data/pkgs" -#define PKG_TMP_DIR "/data/tmp" -#define PKG_TMP_PKG_DIR "/data/tmp/pkg" +#define PKG_REPO_DIR "/etc/nxpkg" +#define PKG_REPO_INDEX "/etc/nxpkg/index.json" +#define PKG_REPO_INSTALLED "/var/lib/nxpkg/installed.json" +#define PKG_STORE_DIR "/var/lib/nxpkg/pkgs" +#define PKG_TMP_DIR "/var/cache/nxpkg" +#define PKG_TMP_PKG_DIR "/var/cache/nxpkg/pkg" #define PKG_NAME_MAX 63 #define PKG_VERSION_MAX 31 diff --git a/system/nxpkg/pkg_compat.c b/system/nxpkg/pkg_compat.c index d093cd2b793..301fb07aacf 100644 --- a/system/nxpkg/pkg_compat.c +++ b/system/nxpkg/pkg_compat.c @@ -52,7 +52,7 @@ int pkg_compat_check(FAR const struct pkg_manifest_s *manifest) if (strcmp(manifest->arch, pkg_runtime_arch()) != 0) { - return -EXDEV; + return -ENOEXEC; } if (strcmp(manifest->compat, pkg_runtime_compat()) != 0) diff --git a/system/nxpkg/pkg_store.c b/system/nxpkg/pkg_store.c index cacfeec2cc5..84680ef3a12 100644 --- a/system/nxpkg/pkg_store.c +++ b/system/nxpkg/pkg_store.c @@ -68,7 +68,12 @@ static int pkg_store_mkdir(FAR const char *path) ret = stat(path, &st); if (ret == 0) { - return S_ISDIR(st.st_mode) ? 0 : -ENOTDIR; + if (!S_ISDIR(st.st_mode)) + { + return -ENOTDIR; + } + + return 0; } if (errno != ENOENT) @@ -85,6 +90,42 @@ static int pkg_store_mkdir(FAR const char *path) return 0; } +static int pkg_store_mkdirs(FAR const char *path) +{ + char buffer[PATH_MAX]; + FAR char *cursor; + int ret; + + ret = snprintf(buffer, sizeof(buffer), "%s", path); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= sizeof(buffer)) + { + return -ENAMETOOLONG; + } + + for (cursor = buffer + 1; *cursor != '\0'; cursor++) + { + if (*cursor != '/') + { + continue; + } + + *cursor = '\0'; + ret = pkg_store_mkdir(buffer); + *cursor = '/'; + if (ret < 0) + { + return ret; + } + } + + return pkg_store_mkdir(buffer); +} + static int pkg_store_write_all(int fd, FAR const char *buffer, size_t length) { size_t offset = 0; @@ -126,31 +167,25 @@ int pkg_store_prepare_layout(void) { int ret; - ret = pkg_store_mkdir("/data"); - if (ret < 0) - { - return ret; - } - - ret = pkg_store_mkdir(PKG_REPO_DIR); + ret = pkg_store_mkdirs(PKG_REPO_DIR); if (ret < 0) { return ret; } - ret = pkg_store_mkdir(PKG_STORE_DIR); + ret = pkg_store_mkdirs(PKG_STORE_DIR); if (ret < 0) { return ret; } - ret = pkg_store_mkdir(PKG_TMP_DIR); + ret = pkg_store_mkdirs(PKG_TMP_DIR); if (ret < 0) { return ret; } - return pkg_store_mkdir(PKG_TMP_PKG_DIR); + return pkg_store_mkdirs(PKG_TMP_PKG_DIR); } int pkg_store_ensure_package_root(FAR const char *name) @@ -164,7 +199,7 @@ int pkg_store_ensure_package_root(FAR const char *name) return ret; } - return pkg_store_mkdir(path); + return pkg_store_mkdirs(path); } int pkg_store_ensure_version_dir(FAR const char *name, @@ -185,7 +220,7 @@ int pkg_store_ensure_version_dir(FAR const char *name, return ret; } - return pkg_store_mkdir(path); + return pkg_store_mkdirs(path); } int pkg_store_format_index_path(FAR char *buffer, size_t size)