aboutsummaryrefslogtreecommitdiff
path: root/src/lib/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/core')
-rw-r--r--src/lib/core/conda.c264
-rw-r--r--src/lib/core/include/conda.h62
-rw-r--r--src/lib/core/include/recipe.h28
-rw-r--r--src/lib/core/include/utils.h2
-rw-r--r--src/lib/core/recipe.c32
-rw-r--r--src/lib/core/system.c4
-rw-r--r--src/lib/core/utils.c80
7 files changed, 353 insertions, 119 deletions
diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c
index 5c7779f..bf27d21 100644
--- a/src/lib/core/conda.c
+++ b/src/lib/core/conda.c
@@ -194,6 +194,31 @@ int pip_exec(const char *args) {
return result;
}
+char *python_importlib_metadata_version(const char *package_name) {
+ int status = 0;
+ char cmd[PATH_MAX] = {0};
+
+ if (strpbrk(package_name, "\\/*{}()|;&\"'\r\n")) {
+ SYSERROR("package name is invalid: '%s'", package_name);
+ return NULL;
+ }
+ snprintf(cmd, sizeof(cmd), "python3 -c 'from importlib.metadata import version; print(version(r\x22%s\x22))'", package_name);
+
+ char *version = shell_output(cmd, &status);
+ if (status) {
+ SYSERROR("version detection failed");
+ guard_free(version);
+ return NULL;
+ }
+ if (!version) {
+ SYSERROR("unable to allocate version");
+ return NULL;
+ }
+ strip(version);
+ return version;
+}
+
+
static const char *PKG_ERROR_STR[] = {
"success",
"[internal] unhandled package manager mode",
@@ -227,21 +252,30 @@ int pkg_index_provides(int mode, const char *index, const char *spec, const char
SYSERROR("Unable to create log directory: %s", logdir ? logdir : "NULL");
return -1;
}
- const char logfile_template[] = "STASIS-package_exists.XXXXXX";
- char logfile[PATH_MAX] = {0};
- snprintf(logfile, sizeof(logfile), "%s/%s", logdir, logfile_template);
- int logfd = mkstemp(logfile);
- if (logfd < 0) {
- SYSERROR("unable to create log file: %s", logfile);
- remove(logfile); // fail harmlessly if not present
- return PKG_INDEX_PROVIDES_E_INTERNAL_LOG_HANDLE;
+ const int stdout_stream = 0;
+ const int stderr_stream = 1;
+ const int stdout_st = 0;
+ //const int stderr_st = 1;
+ char logfile[2][PATH_MAX] = {0};
+ int logfile_fd[2] = {-1, -1};
+ struct stat logfile_st[2] = {0};
+
+ for (size_t i = 0; i < sizeof(logfile) / sizeof(logfile[0]); i++) {
+ const char logfile_template[] = "STASIS-package_exists.XXXXXX";
+ snprintf(logfile[i], sizeof(logfile[i]), "%s/%s", logdir, logfile_template);
+ logfile_fd[i] = mkstemp(logfile[i]);
+ if (logfile_fd[i] < 0) {
+ SYSERROR("unable to create log file: %s", logfile[i]);
+ remove(logfile[i]); // fail harmlessly if not present
+ return PKG_INDEX_PROVIDES_E_INTERNAL_LOG_HANDLE;
+ }
}
int status = 0;
struct Process proc = {0};
- proc.redirect_stderr = 1;
- snprintf(proc.f_stdout, sizeof(proc.f_stdout), "%s", logfile);
+ snprintf(proc.f_stdout, sizeof(proc.f_stdout), "%s", logfile[stdout_stream]);
+ snprintf(proc.f_stderr, sizeof(proc.f_stderr), "%s", logfile[stderr_stream]);
if (mode == PKG_USE_PIP) {
// Do an installation in dry-run mode to see if the package exists in the given index.
@@ -268,26 +302,40 @@ int pkg_index_provides(int mode, const char *index, const char *spec, const char
SYSDEBUG("Executing: %s", cmd);
status = shell(&proc, cmd);
- SYSDEBUG("Log file: %s", logfile);
- if (status != 0) {
- FILE *fp = fdopen(logfd, "r");
- if (!fp) {
- remove(logfile);
+ // Populate stat data for log files
+ for (size_t i = 0; i < sizeof(logfile) / sizeof(logfile[0]); i++) {
+ if (stat(logfile[i], &logfile_st[i])) {
+ SYSERROR("Unable to stat %s", logfile[i]);
return -1;
}
+ }
+
+ if (status != 0) {
+ SYSERROR("Command exited non-zero (%d)", status);
+ for (size_t i = 0; i < sizeof(logfile) / sizeof(logfile[0]); i++) {
+ const char *stream_name = i == 0 ? "stdout" : "stderr";
+ if (!logfile_st[i].st_size) {
+ continue;
+ }
+ SYSDEBUG("(%s): %s", stream_name, logfile[i]);
+ FILE *fp = fdopen(logfile_fd[i], "r");
+ if (!fp) {
+ remove(logfile[i]);
+ return -1;
+ }
- fflush(stdout);
- fflush(stderr);
+ fflush(stdout);
+ fflush(stderr);
- char line[STASIS_BUFSIZ] = {0};
- while (fgets(line, sizeof(line) - 1, fp) != NULL) {
- SYSDEBUG("%s", strip(line));
- }
+ char line[STASIS_BUFSIZ] = {0};
+ while (fgets(line, sizeof(line) - 1, fp) != NULL) {
+ SYSINFO("(%s): %s", stream_name, strip(line));
+ }
- fflush(stderr);
- fclose(fp);
+ fflush(stderr);
+ fclose(fp);
+ }
}
- remove(logfile);
if (WTERMSIG(proc.returncode)) {
// This gets its own return value because if the external program
@@ -295,18 +343,40 @@ int pkg_index_provides(int mode, const char *index, const char *spec, const char
return PKG_INDEX_PROVIDES_E_MANAGER_SIGNALED;
}
+ int final = PKG_FOUND;
if (status < 0) {
- return PKG_INDEX_PROVIDES_E_MANAGER_EXEC;
+ final = PKG_INDEX_PROVIDES_E_MANAGER_EXEC;
} else if (WEXITSTATUS(proc.returncode) > 1) {
// Pip and conda both return 2 on argument parsing errors
- return PKG_INDEX_PROVIDES_E_MANAGER_RUNTIME;
+ final = PKG_INDEX_PROVIDES_E_MANAGER_RUNTIME;
+ } else if (logfile_st[stdout_st].st_size > 1 && WEXITSTATUS(proc.returncode) == 0) {
+ // modern mamba return zero when a package not found.
+ // even more ridiculous; the error messages are written to stdout, not stderr
+ struct StrList *output = strlist_init();
+ if (!output) {
+ SYSERROR("unable to allocate memory for stdout log list");
+ return -1;
+ }
+ if (strlist_append_file(output, logfile[stdout_stream], NULL)) {
+ strlist_free(&output);
+ SYSERROR("unable to append stdout log to list: %s", logfile[stdout_stream]);
+ return -1;
+ }
+ if (strlist_contains(output, "No entries", NULL)) {
+ final = PKG_NOT_FOUND;
+ }
+ strlist_free(&output);
} else if (WEXITSTATUS(proc.returncode) == 1) {
// Pip and conda both return 1 when a package is not found.
// Unfortunately this applies to botched version specs, too.
- return PKG_NOT_FOUND;
- } else {
- return PKG_FOUND;
+ final = PKG_NOT_FOUND;
}
+
+ // Remove log files
+ for (size_t i = 0; i < sizeof(logfile) / sizeof(logfile[0]); i++) {
+ remove(logfile[stdout_stream]);
+ }
+ return final;
}
int conda_exec(const char *args) {
@@ -326,14 +396,22 @@ int conda_exec(const char *args) {
};
char conda_as[10] = {0};
+ const char *last_mamba_command = NULL;
safe_strncpy(conda_as, "conda", sizeof(conda_as));
for (size_t i = 0; mamba_commands[i] != NULL; i++) {
if (startswith(args, mamba_commands[i])) {
+ last_mamba_command = mamba_commands[i];
safe_strncpy(conda_as, "mamba", sizeof(conda_as));
break;
}
}
+ const int boa_present = find_program("boa") != NULL;
+ if (!boa_present) {
+ if (last_mamba_command && !strcmp(last_mamba_command, "build")) {
+ safe_strncpy(conda_as, "conda", sizeof(conda_as));
+ }
+ }
const char *command_fmt = "%s %s";
const int len = snprintf(NULL, 0, command_fmt, conda_as, args);
@@ -416,15 +494,12 @@ static int env0_to_runtime(const char *logfile) {
int conda_activate(const char *root, const char *env_name) {
const char *init_script_conda = "/etc/profile.d/conda.sh";
- const char *init_script_mamba = "/etc/profile.d/mamba.sh";
char path_conda[PATH_MAX] = {0};
- char path_mamba[PATH_MAX] = {0};
char logfile[PATH_MAX] = {0};
struct Process proc = {0};
// Where to find conda's init scripts
snprintf(path_conda, sizeof(path_conda), "%s%s", root, init_script_conda);
- snprintf(path_mamba, sizeof(path_mamba), "%s%s", root, init_script_mamba);
// Set the path to our stdout log
// Emulate mktemp()'s behavior. Give us a unique file name, but don't use
@@ -448,12 +523,6 @@ int conda_activate(const char *root, const char *env_name) {
return -1;
}
- if (access(path_mamba, F_OK) < 0) {
- SYSERROR("mamba is missing: %s, %s", path_mamba, strerror(errno));
- remove(logfile);
- return -1;
- }
-
// Fully activate conda and record its effect on the runtime environment
char command[PATH_MAX * 3];
const char *conda_shlvl_str = getenv("CONDA_SHLVL");
@@ -475,20 +544,19 @@ int conda_activate(const char *root, const char *env_name) {
snprintf(command, sizeof(command),
"set -a\n"
"source %s\n"
- "__conda_exe() (\n"
- " \"$CONDA_PYTHON_EXE\" \"$CONDA_EXE\" $_CE_M $_CE_CONDA \"$@\"\n"
- ")\n\n"
- "export -f __conda_exe\n"
- "source %s\n"
- "__mamba_exe() (\n"
- " \\local MAMBA_CONDA_EXE_BACKUP=$CONDA_EXE\n"
- " \\local MAMBA_EXE=$(\\dirname \"${CONDA_EXE}\")/mamba\n"
- " \"$CONDA_PYTHON_EXE\" \"$MAMBA_EXE\" $_CE_M $_CE_CONDA \"$@\"\n"
- ")\n\n"
- "export -f __mamba_exe\n"
+ "tempfile=$(mktemp)\n"
+ "chmod 600 \"${tempfile}\"\n"
+ "%s/bin/mamba shell init --shell bash --dry-run 2>/dev/null\\\n"
+ "| (ignore=1; \\\n"
+ " while read line; do \\\n"
+ " if [[ \"$line\" == \"#\"* ]]; then ignore=0; fi; \\\n"
+ " if (( ignore == 0 )); then echo $line; fi; \\\n"
+ " done) 1> \"${tempfile}\"\n"
+ "source \"${tempfile}\"\n"
+ "rm -f \"${tempfile}\"\n"
"%s\n"
"conda activate %s 1>&2\n"
- "env -0\n", path_conda, path_mamba, conda_shlvl ? "conda deactivate" : ":", env_name);
+ "env -0\n", path_conda, root, conda_shlvl ? "conda deactivate" : ":", env_name);
int retval = shell(&proc, command);
if (retval) {
@@ -561,7 +629,20 @@ int conda_check_required() {
return 0;
}
-int conda_setup_headless() {
+int conda_setup_headless(struct CondaCapabilities *cc) {
+ mkdirs(cc->prefix, 0755);
+
+ char rcpath[PATH_MAX];
+ snprintf(rcpath, sizeof(rcpath), "%s/.condarc", cc->prefix);
+ touch(rcpath);
+ if (errno == ENOENT) {
+ errno = 0;
+ }
+
+ setenv("CONDARC", rcpath, 1);
+ setenv("MAMBARC", rcpath, 1);
+ setenv("MAMBA_ROOT_PREFIX", cc->prefix, 1);
+
if (globals.verbose) {
conda_exec("config --system --set quiet false");
} else {
@@ -582,6 +663,18 @@ int conda_setup_headless() {
size_t total = 0;
const char *cmd_fmt = "'%s'";
if (globals.conda_packages && strlist_count(globals.conda_packages)) {
+ // Push or pop build packages based on capabilities
+ size_t boa_index = 0;
+ const int boa_requested = strlist_contains(globals.conda_packages, "boa", &boa_index);
+ if (boa_requested && !cc->require_boa) {
+ SYSWARN("Removing boa from global package list due to incompatible conda version (too new): %s", cc->conda_version);
+ strlist_remove(globals.conda_packages, boa_index);
+ } else if (!boa_requested && cc->require_boa) {
+ SYSWARN("Adding boa to global package list");
+ strlist_append(&globals.conda_packages, "boa");
+ }
+
+
memset(cmd, 0, sizeof(cmd));
safe_strncpy(cmd, "install ", sizeof(cmd));
@@ -719,7 +812,18 @@ int conda_env_remove(char *name) {
int conda_env_export(char *name, char *output_dir, char *output_filename) {
char env_command[PATH_MAX];
- snprintf(env_command, sizeof(env_command), "env export -n %s -f %s/%s.yml", name, output_dir, output_filename);
+ int vr = 0;
+ const char *env_format = NULL;
+ char *version = shell_output("conda --version", &vr);
+ if (version) {
+ const size_t v_offset = strlen("conda ");
+ if (version_compare(GT, version + v_offset, "25.1.0")) {
+ env_format = "--format=yml";
+ }
+ guard_free(version);
+ }
+
+ snprintf(env_command, sizeof(env_command), "env export %s -n %s -f %s/%s.yml", env_format ? env_format : "", name, output_dir, output_filename);
return conda_exec(env_command);
}
@@ -749,3 +853,59 @@ int conda_env_exists(const char *root, const char *name) {
snprintf(path, sizeof(path), "%s/envs/%s", root, name);
return access(path, F_OK) == 0;
}
+
+void conda_capable_free(struct CondaCapabilities *ccap) {
+ guard_free(ccap->conda_version);
+ guard_free(ccap->mamba_version);
+ memset(ccap, 0, sizeof(*ccap));
+}
+
+int conda_capable(struct CondaCapabilities *ccap, const char *root) {
+ struct CondaCapabilities *cc = ccap;
+ memset(cc, 0, sizeof(*cc));
+
+ if (find_program("conda")) {
+ cc->available = true;
+ }
+
+ if (cc->available) {
+ char *conda_version = python_importlib_metadata_version("conda");
+ if (!conda_version) {
+ SYSERROR("conda version detection failed");
+ return -1;
+ }
+
+ char *mamba_version = python_importlib_metadata_version("libmambapy");
+ if (!mamba_version) {
+ SYSERROR("unable to allocate mamba_version");
+ guard_free(conda_version);
+ return -1;
+ }
+
+ cc->prefix = root;
+ cc->conda_version = strdup(conda_version);
+ cc->mamba_version = strdup(mamba_version);
+ if (version_compare(GT | EQ, cc->conda_version, "25.3.0")) {
+ cc->require_explicit_export_format = true;
+ cc->require_boa = false;
+ } else {
+ cc->require_explicit_export_format = false;
+ cc->require_manual_activation_shim = true;
+ cc->require_boa = true;
+ cc->require_libmamba_solver = true;
+ }
+
+ struct Process proc = {0};
+ safe_strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr));
+ safe_strncpy(proc.f_stdout, "/dev/null", sizeof(proc.f_stdout));
+ if (shell(&proc, "mamba install --use-local --dry-run conda")) {
+ cc->missing_use_local = true;
+ }
+
+ cc->usable = true;
+
+ guard_free(mamba_version);
+ guard_free(conda_version);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/src/lib/core/include/conda.h b/src/lib/core/include/conda.h
index a7108ec..9a6178b 100644
--- a/src/lib/core/include/conda.h
+++ b/src/lib/core/include/conda.h
@@ -26,6 +26,48 @@
#define PKG_INDEX_PROVIDES_E_MANAGER_EXEC (PKG_INDEX_PROVIDES_ERROR_MESSAGE_OFFSET + 5)
#define PKG_INDEX_PROVIDES_FAILED(ECODE) ((ECODE) <= PKG_INDEX_PROVIDES_ERROR_MESSAGE_OFFSET)
+/**
+ * @struct CondaCapabilities
+ * @brief Collection of feature flags to support older, newer, and transitional versions of conda/mamba.
+ * @see implementation in `conda_capable`
+ */
+struct CondaCapabilities {
+ /// Conda installation prefix
+ const char *prefix;
+ /// Currently installed version of Conda
+ char *conda_version;
+ /// Currently installed version of Mamba
+ char *mamba_version;
+ /// Is conda available in the runtime environment?
+ bool available;
+ /// Can conda execute?
+ bool usable;
+ /// Do we need to pass extra arguments to "conda env export"?
+ bool require_explicit_export_format;
+ /// Do we need to inject our own shim to make Conda available?
+ bool require_manual_activation_shim;
+ /// Does this version of Conda support building with boa?
+ bool require_boa;
+ /// Does this version of Conda need to be configured to use libmamba?
+ bool require_libmamba_solver;
+ /// Does "mamba install --use-local" work on this version of mamba?
+ bool missing_use_local;
+};
+
+/**
+ * Check for the existence of "Conda" and set flags based on the availability of features
+ * @param ccap a pointer to an empty `struct CondaCapabilities`
+ * @param root conda installation root directory
+ * @return 0 on success
+ */
+int conda_capable(struct CondaCapabilities *ccap, const char *root);
+
+/**
+ * Free memory allocated by `conda_capable`
+ * @param ccap a pointer to a populated `struct CondaCapabilities`
+ */
+void conda_capable_free(struct CondaCapabilities *ccap);
+
struct MicromambaInfo {
char *micromamba_prefix; //!< Path to write micromamba binary
char *conda_prefix; //!< Path to install conda base tree
@@ -83,6 +125,24 @@ int python_exec(const char *args);
int pip_exec(const char *args);
/**
+ * Use importlib to resolve the version of an installed package
+ *
+ * ```c
+ * char *numpy_version = python_importlib_metadata_version("numpy");
+ * if (!numpy_version) {
+ * fprintf(stderr, "failed to get numpy version\n");
+ * exit(1);
+ * }
+ *
+ * printf("numpy version: %s\n", numpy_version);
+ * free(numpy_version);
+ * ```
+ * @param package_name of installed python package
+ * @return
+ */
+char *python_importlib_metadata_version(const char *package_name);
+
+/**
* Execute conda (or if possible, mamba)
* Conda/Mamba is determined by PATH
*
@@ -117,7 +177,7 @@ int conda_activate(const char *root, const char *env_name);
/**
* Configure the active conda installation for headless operation
*/
-int conda_setup_headless();
+int conda_setup_headless(struct CondaCapabilities *cc);
/**
* Creates a Conda environment from a YAML config
diff --git a/src/lib/core/include/recipe.h b/src/lib/core/include/recipe.h
index 4dea248..ddcbabf 100644
--- a/src/lib/core/include/recipe.h
+++ b/src/lib/core/include/recipe.h
@@ -6,13 +6,20 @@
#include "utils.h"
//! Unable to determine recipe repo type
-#define RECIPE_TYPE_UNKNOWN 0
+#define RECIPE_STYLE_UNKNOWN 0
//! Recipe repo is from conda-forge
-#define RECIPE_TYPE_CONDA_FORGE 1
+#define RECIPE_STYLE_CONDA_FORGE 1
//! Recipe repo is from astroconda
-#define RECIPE_TYPE_ASTROCONDA 2
+#define RECIPE_STYLE_ASTROCONDA 2
//! Recipe repo provides the required build configurations but doesn't match conda-forge or astroconda's signature
-#define RECIPE_TYPE_GENERIC 3
+#define RECIPE_STYLE_GENERIC 3
+
+//! Unable to determine required build system
+#define RECIPE_BUILD_UNKNOWN 0
+//! Build uses meta.yaml
+#define RECIPE_BUILD_CONDA_BUILD 1
+//! Build uses recipe.yaml
+#define RECIPE_BUILD_RATTLER 2
/**
* Download a Conda package recipe
@@ -46,18 +53,18 @@ int recipe_clone(char *recipe_dir, char *url, char *gitref, char **result);
* }
*
* int recipe_type;
- * recipe_type = recipe_get_type(recipe);
+ * recipe_type = recipe_get_style(recipe);
* switch (recipe_type) {
- * case RECIPE_TYPE_CONDA_FORGE:
+ * case RECIPE_STYLE_CONDA_FORGE:
* // do something specific for conda-forge directory structure
* break;
- * case RECIPE_TYPE_ASTROCONDA:
+ * case RECIPE_STYLE_ASTROCONDA:
* // do something specific for astroconda directory structure
* break;
- * case RECIPE_TYPE_GENERIC:
+ * case RECIPE_STYLE_GENERIC:
* // do something specific for a directory containing a meta.yaml config
* break;
- * case RECIPE_TYPE_UNKNOWN:
+ * case RECIPE_STYLE_UNKNOWN:
* default:
* // the structure is foreign or the path doesn't contain a conda recipe
* break;
@@ -67,6 +74,7 @@ int recipe_clone(char *recipe_dir, char *url, char *gitref, char **result);
* @param repopath path to git repository containing conda recipe(s)
* @return One of RECIPE_TYPE_UNKNOWN, RECIPE_TYPE_CONDA_FORGE, RECIPE_TYPE_ASTROCONDA, RECIPE_TYPE_GENERIC
*/
-int recipe_get_type(char *repopath);
+int recipe_get_style(char *repopath);
+int recipe_get_build_system(const char *repopath, int style);
#endif //STASIS_RECIPE_H
diff --git a/src/lib/core/include/utils.h b/src/lib/core/include/utils.h
index 3f0fe9f..e75995a 100644
--- a/src/lib/core/include/utils.h
+++ b/src/lib/core/include/utils.h
@@ -429,7 +429,7 @@ int gen_file_extension_str(char *filename, size_t maxlen, const char *extension)
*/
char *remove_extras(char *s);
-void debug_hexdump(char *data, int len);
+void debug_hexdump(char *data, size_t len);
/**
* Realloc helper
diff --git a/src/lib/core/recipe.c b/src/lib/core/recipe.c
index 8cc8e21..2e18492 100644
--- a/src/lib/core/recipe.c
+++ b/src/lib/core/recipe.c
@@ -34,8 +34,26 @@ int recipe_clone(char *recipe_dir, char *url, char *gitref, char **result) {
return git_clone(&proc, url, destdir, gitref);
}
+int recipe_get_build_system(const char *repopath, const int style) {
+ char filename[PATH_MAX] = {0};
+ safe_strncat(filename, repopath, sizeof(filename));
-int recipe_get_type(char *repopath) {
+ if (style == RECIPE_STYLE_CONDA_FORGE) {
+ safe_strncat(filename, "/recipe/recipe.yaml", sizeof(filename));
+ if (!access(filename, F_OK)) {
+ return RECIPE_BUILD_RATTLER;
+ }
+ return RECIPE_BUILD_CONDA_BUILD;
+ }
+
+ if (style == RECIPE_STYLE_ASTROCONDA || style == RECIPE_STYLE_GENERIC) {
+ return RECIPE_BUILD_CONDA_BUILD;
+ }
+
+ return RECIPE_BUILD_UNKNOWN;
+}
+
+int recipe_get_style(char *repopath) {
// conda-forge is a collection of repositories
// "conda-forge.yml" is guaranteed to exist
const char *marker[] = {
@@ -44,10 +62,10 @@ int recipe_get_type(char *repopath) {
"meta.yaml",
NULL
};
- const int type[] = {
- RECIPE_TYPE_CONDA_FORGE,
- RECIPE_TYPE_ASTROCONDA,
- RECIPE_TYPE_GENERIC
+ const int style[] = {
+ RECIPE_STYLE_CONDA_FORGE,
+ RECIPE_STYLE_ASTROCONDA,
+ RECIPE_STYLE_GENERIC
};
for (size_t i = 0; marker[i] != NULL; i++) {
@@ -55,9 +73,9 @@ int recipe_get_type(char *repopath) {
snprintf(path, sizeof(path), "%s/%s", repopath, marker[i]);
int result = access(path, F_OK);
if (!result) {
- return type[i];
+ return style[i];
}
}
- return RECIPE_TYPE_UNKNOWN;
+ return RECIPE_STYLE_UNKNOWN;
} \ No newline at end of file
diff --git a/src/lib/core/system.c b/src/lib/core/system.c
index 4f1ece6..542f9fd 100644
--- a/src/lib/core/system.c
+++ b/src/lib/core/system.c
@@ -37,7 +37,9 @@ int shell(struct Process *proc, char *args) {
// Set the script's permissions so that only the calling user can use it
// This should help prevent eavesdropping if keys are applied in plain-text
// somewhere.
- chmod(t_name, 0700);
+ if (chmod(t_name, 0700)) {
+ SYSWARN("unable to change script permissions: %s, %s", t_name, strerror(errno));
+ }
pid_t pid = fork();
if (pid == -1) {
diff --git a/src/lib/core/utils.c b/src/lib/core/utils.c
index 31208ad..462604d 100644
--- a/src/lib/core/utils.c
+++ b/src/lib/core/utils.c
@@ -946,57 +946,43 @@ int gen_file_extension_str(char *filename, const size_t maxlen, const char *exte
return replace_text(ext_orig, ext_orig, extension, 0);
}
-#define DEBUG_HEXDUMP_FMT_BYTES 6
-#define DEBUG_HEXDUMP_ADDR_MAXLEN 20
-#define DEBUG_HEXDUMP_BYTES_MAXLEN (16 * 3 + 2)
-#define DEBUG_HEXDUMP_ASCII_MAXLEN (16 + 1)
-#define DEBUG_HEXDUMP_OUTPUT_MAXLEN (DEBUG_HEXDUMP_FMT_BYTES + DEBUG_HEXDUMP_ADDR_MAXLEN + DEBUG_HEXDUMP_BYTES_MAXLEN + DEBUG_HEXDUMP_ASCII_MAXLEN + 1)
-
-void debug_hexdump(char *data, int len) {
- int count = 0;
- char addr[DEBUG_HEXDUMP_ADDR_MAXLEN] = {0};
- char bytes[DEBUG_HEXDUMP_BYTES_MAXLEN] = {0};
- char ascii[DEBUG_HEXDUMP_ASCII_MAXLEN] = {0};
- char output[DEBUG_HEXDUMP_OUTPUT_MAXLEN] = {0};
+void debug_hexdump(char *data, const size_t len) {
+ if (!data) {
+ SYSWARN("DATA NULL\n");
+ return;
+ }
+ if (len <= 0) {
+ SYSWARN("ZERO LENGTH\n");
+ return;
+ }
+ const size_t window = 16;
+ size_t avail = len;
+ const size_t chunk = avail / window ? window : avail;
char *start = data;
- char *end = data + len;
-
- char *pos = start;
- while (pos != end) {
- if (count == 0) {
- snprintf(addr + strlen(addr), sizeof(addr) - strlen(addr), "%p", pos);
+ while (avail > 0) {
+ const size_t need = chunk > avail ? avail : chunk;
+ char *p = start;
+ printf("%p | ", p);
+ size_t j = 0;
+ for (j = 0; j < need; j++) {
+ printf("%02x ", p[j]);
}
- if (count == 8) {
- safe_strncat(bytes, " ", sizeof(bytes));
+ const size_t padding = window - j;
+ for (size_t i = 0; i < padding; i++) {
+ printf("00 ");
}
- if (count > 15) {
- snprintf(output, sizeof(output), "%s | %s | %s", addr, bytes, ascii);
- puts(output);
- memset(output, 0, sizeof(output));
- memset(addr, 0, sizeof(addr));
- memset(bytes, 0, sizeof(bytes));
- memset(ascii, 0, sizeof(ascii));
- count = 0;
- continue;
+ printf("| ");
+ for (j = 0; j < need; j++) {
+ char *c = p + j;
+ printf("%c", isprint(*c) ? *c : '.');
}
-
- snprintf(bytes + strlen(bytes), sizeof(bytes) - strlen(bytes), "%02X ", (unsigned char) *pos);
- snprintf(ascii + strlen(ascii), sizeof(ascii) - strlen(ascii), "%c", isprint(*pos) ? *pos : '.');
-
- pos++;
- count++;
- }
-
- if (count <= 8) {
- // Add group padding
- safe_strncat(bytes, " ", sizeof(bytes));
- }
- const int padding = 16 - count;
- for (int i = 0; i < padding; i++) {
- safe_strncat(bytes, " ", sizeof(bytes));
+ for (size_t i = 0; i < padding; i++) {
+ printf(".");
+ }
+ printf(" |\n");
+ avail -= need;
+ start += need;
}
- snprintf(output, sizeof(output), "%s | %s | %s", addr, bytes, ascii);
- puts(output);
}
int grow(const size_t size_new, size_t *size_orig, char **data) {
@@ -1295,7 +1281,7 @@ int is_file_compressed(const char *filename) {
{(unsigned char *) "PK\03\04", 3}, // zip
{(unsigned char *) "PK\05\06", 3}, // zip (empty)
{(unsigned char *) "PK\07\08", 3}, // zip (spanned)
- {(unsigned char *) "\xfd\x2f\xb5\x28", 4} // zstd
+ {(unsigned char *) "\x28\xb5\x2f\xfd", 4} // zstd
};
unsigned char buf[8] = {0}; // unsigned long
size_t bytes_read = 0;