diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cli/stasis/stasis_main.c | 36 | ||||
| -rw-r--r-- | src/lib/core/conda.c | 154 | ||||
| -rw-r--r-- | src/lib/core/utils.c | 42 | 
3 files changed, 160 insertions, 72 deletions
| diff --git a/src/cli/stasis/stasis_main.c b/src/cli/stasis/stasis_main.c index e188b2e..093e32e 100644 --- a/src/cli/stasis/stasis_main.c +++ b/src/cli/stasis/stasis_main.c @@ -225,14 +225,6 @@ int main(int argc, char *argv[]) {          exit(1);      } -    msg(STASIS_MSG_L1, "Conda setup\n"); -    delivery_get_conda_installer_url(&ctx, installer_url); -    msg(STASIS_MSG_L2, "Downloading: %s\n", installer_url); -    if (delivery_get_conda_installer(&ctx, installer_url)) { -        msg(STASIS_MSG_ERROR, "download failed: %s\n", installer_url); -        exit(1); -    } -      // Unlikely to occur: this should help prevent rmtree() from destroying your entire filesystem      // if path is "/" then, die      // or if empty string, die @@ -241,6 +233,30 @@ int main(int argc, char *argv[]) {          exit(1);      } +    // 2 = #! +    // 5 = /bin\n +    const size_t prefix_len = strlen(ctx.storage.conda_install_prefix) + 2 + 5; +    const size_t prefix_len_max = 127; +    msg(STASIS_MSG_L1, "Checking length of conda installation prefix\n"); +    if (!strcmp(ctx.system.platform[DELIVERY_PLATFORM], "Linux") && prefix_len > prefix_len_max) { +        msg(STASIS_MSG_L2 | STASIS_MSG_ERROR, +            "The shebang, '#!%s/bin/python\\n' is too long (%zu > %zu).\n", +            ctx.storage.conda_install_prefix, prefix_len, prefix_len_max); +        msg(STASIS_MSG_L2 | STASIS_MSG_ERROR, +            "Conda's workaround to handle long path names does not work consistently within STASIS.\n"); +        msg(STASIS_MSG_L2 | STASIS_MSG_ERROR, +            "Please try again from a different, \"shorter\", directory.\n"); +        exit(1); +    } + +    msg(STASIS_MSG_L1, "Conda setup\n"); +    delivery_get_conda_installer_url(&ctx, installer_url); +    msg(STASIS_MSG_L2, "Downloading: %s\n", installer_url); +    if (delivery_get_conda_installer(&ctx, installer_url)) { +        msg(STASIS_MSG_ERROR, "download failed: %s\n", installer_url); +        exit(1); +    } +      msg(STASIS_MSG_L2, "Installing: %s\n", ctx.conda.installer_name);      delivery_install_conda(ctx.conda.installer_path, ctx.storage.conda_install_prefix); @@ -294,7 +310,7 @@ int main(int argc, char *argv[]) {      }      if (!isempty(ctx.meta.based_on)) { -        if (conda_env_remove(env_name)) { +        if (conda_env_exists(ctx.storage.conda_install_prefix, env_name) && conda_env_remove(env_name)) {              msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "failed to remove release environment: %s\n", env_name);              exit(1);          } @@ -305,7 +321,7 @@ int main(int argc, char *argv[]) {              exit(1);          } -        if (conda_env_remove(env_name_testing)) { +        if (conda_env_exists(ctx.storage.conda_install_prefix, env_name_testing) && conda_env_remove(env_name_testing)) {              msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "failed to remove testing environment %s\n", env_name_testing);              exit(1);          } diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c index 435af35..b2caa63f 100644 --- a/src/lib/core/conda.c +++ b/src/lib/core/conda.c @@ -208,25 +208,70 @@ int conda_exec(const char *args) {  }  static int conda_prepend_bin(const char *root) { -    const char *system_path_old = getenv("PATH");      char conda_bin[PATH_MAX] = {0}; -    snprintf(conda_bin, sizeof(conda_bin) - 1, "%s/bin:%s/condabin", root, root); +    snprintf(conda_bin, sizeof(conda_bin) - 1, "%s/bin", root); +    if (env_manipulate_pathstr("PATH", conda_bin, PM_PREPEND | PM_ONCE)) { +        return -1; +    } +    return 0; +} -    if (!strstr(system_path_old, conda_bin)) { -        // conda_bin is not present in PATH. Add it to the head. -        char system_path_new[STASIS_BUFSIZ]; -        sprintf(system_path_new, "%s:%s", conda_bin, system_path_old); -        if (setenv("PATH", system_path_new, 1) < 0) { -            SYSERROR("Unable to prepend to PATH: %s", conda_bin); +static int conda_prepend_condabin(const char *root) { +    char conda_condabin[PATH_MAX] = {0}; + +    snprintf(conda_condabin, sizeof(conda_condabin) - 1, "%s/condabin", root); +    if (env_manipulate_pathstr("PATH", conda_condabin, PM_PREPEND | PM_ONCE)) { +        return -1; +    } +    return 0; +} + +static int env0_to_runtime(const char *logfile) { +    FILE *fp = fopen(logfile, "r"); +    if (!fp) { +        perror(logfile); +        return -1; +    } + +    while (!feof(fp)) { +        char buf[STASIS_BUFSIZ] = {0}; +        int ch = 0; +        size_t z = 0; +        // We are ingesting output from "env -0" and can't use fgets() +        // Copy each character into the buffer until we encounter '\0' or EOF +        while (z < sizeof(buf) && (ch = (int) fgetc(fp)) != 0) { +            if (ch == EOF) { +                break; +            } +            buf[z] = (char) ch; +            z++; +        } +        buf[strlen(buf)] = 0; + +        if (!strlen(buf)) { +            continue; +        } + +        char **part = split(buf, "=", 1); +        if (!part) { +            perror("unable to split environment variable buffer");              return -1;          } +        if (!part[0]) { +            msg(STASIS_MSG_WARN | STASIS_MSG_L1, "Invalid environment variable key ignored: '%s'\n", buf); +        } else if (!part[1]) { +            msg(STASIS_MSG_WARN | STASIS_MSG_L1, "Invalid environment variable value ignored: '%s'\n", buf); +        } else { +            setenv(part[0], part[1], 1); +        } +        GENERIC_ARRAY_FREE(part);      } +    fclose(fp);      return 0;  }  int conda_activate(const char *root, const char *env_name) { -    FILE *fp = NULL;      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}; @@ -266,24 +311,42 @@ int conda_activate(const char *root, const char *env_name) {          return -1;      } -    if (conda_prepend_bin(root)) { +    // Fully activate conda and record its effect on the runtime environment +    char command[PATH_MAX * 3]; +    const char *conda_shlvl_str = getenv("CONDA_SHLVL"); +    unsigned long conda_shlvl = 0; +    if (conda_shlvl_str) { +        conda_shlvl = strtol(conda_shlvl_str, NULL, 10); +    } + +    if (conda_prepend_condabin(root)) {          remove(logfile);          return -1;      } -    // Fully activate conda and record its effect on the runtime environment -    char command[PATH_MAX * 3]; -    const char *conda_shlvl = getenv("CONDA_SHLVL"); -    if (conda_shlvl == NULL || strcmp(conda_shlvl, "0") == 0) { -        // First-run initialization -        snprintf(command, sizeof(command) - 1, "source %s; source %s; conda activate %s &>/dev/null; env -0", path_conda, path_mamba, env_name); -    } else { -        // Conda is already available and configured. -        // Make calls directly to conda using conda's base interpreter. -        // The shell functions generated by sourcing path_conda and path_mamba are extremely inconsistent -        // in this environment. DO NOT USE THEM. -        snprintf(command, sizeof(command) - 1, "$CONDA_PYTHON_EXE $CONDA_EXE activate %s &>/dev/null; env -0", env_name); +    if (conda_prepend_bin(root)) { +        remove(logfile); +        return -1;      } + +    snprintf(command, sizeof(command) - 1, +        "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" +        "%s\n" +        "conda activate %s 1>&2\n" +        "env -0\n", path_conda, path_mamba, conda_shlvl ? "conda deactivate" : ":", env_name); +      int retval = shell(&proc, command);      if (retval) {          // it didn't work; drop out for cleanup @@ -294,47 +357,9 @@ int conda_activate(const char *root, const char *env_name) {      // Parse the log file:      // 1. Extract the environment keys and values from the sub-shell      // 2. Apply it to STASIS's runtime environment -    // 3. Now we're ready to execute conda commands anywhere -    fp = fopen(proc.f_stdout, "r"); -    if (!fp) { -        perror(logfile); +    if (env0_to_runtime(logfile) < 0) {          return -1;      } - -    while (!feof(fp)) { -        char buf[STASIS_BUFSIZ] = {0}; -        int ch = 0; -        size_t z = 0; -        // We are ingesting output from "env -0" and can't use fgets() -        // Copy each character into the buffer until we encounter '\0' or EOF -        while (z < sizeof(buf) && (ch = (int) fgetc(fp)) != 0) { -            if (ch == EOF) { -                break; -            } -            buf[z] = (char) ch; -            z++; -        } -        buf[strlen(buf)] = 0; - -        if (!strlen(buf)) { -            continue; -        } - -        char **part = split(buf, "=", 1); -        if (!part) { -            perror("unable to split environment variable buffer"); -            return -1; -        } -        if (!part[0]) { -            msg(STASIS_MSG_WARN | STASIS_MSG_L1, "Invalid environment variable key ignored: '%s'\n", buf); -        } else if (!part[1]) { -            msg(STASIS_MSG_WARN | STASIS_MSG_L1, "Invalid environment variable value ignored: '%s'\n", buf); -        } else { -            setenv(part[0], part[1], 1); -        } -        GENERIC_ARRAY_FREE(part); -    } -    fclose(fp);      remove(logfile);      return 0;  } @@ -346,7 +371,6 @@ int conda_check_required() {      const char *conda_minimum_viable_tools[] = {              "boa",              "conda-build", -            "conda-verify",              NULL      }; @@ -518,3 +542,9 @@ int conda_index(const char *path) {      sprintf(command, "index %s", path);      return conda_exec(command);  } + +int conda_env_exists(const char *root, const char *name) { +    char path[PATH_MAX] = {0}; +    snprintf(path, sizeof(path) - 1 - 6, "%s/envs/%s", root, name); +    return access(path, F_OK) == 0; +} diff --git a/src/lib/core/utils.c b/src/lib/core/utils.c index 5f0807c..18731e6 100644 --- a/src/lib/core/utils.c +++ b/src/lib/core/utils.c @@ -808,3 +808,45 @@ int mkdirs(const char *_path, mode_t mode) {  char *find_version_spec(char *str) {      return strpbrk(str, "@~=<>!");  } + +int env_manipulate_pathstr(const char *key, char *path, int mode) { +    if (isempty(path)) { +        SYSERROR("%s", "New PATH element cannot be zero-length or NULL"); +        return -1; +    } + +    const char *system_path_old = getenv("PATH"); +    if (!system_path_old) { +        SYSERROR("%s", "Unable to read PATH"); +        return -1; +    } + +    char *system_path_new = NULL; + +    if (mode & PM_APPEND) { +        asprintf(&system_path_new, "%s%s%s", system_path_old, PATH_SEP, path); +    } else if (mode & PM_PREPEND) { +        asprintf(&system_path_new, "%s%s%s", path, PATH_SEP, system_path_old); +    } + +    if (!system_path_new) { +        SYSERROR("%s", "Unable to allocate memory to update PATH"); +        return -1; +    } + +    if (mode & PM_ONCE) { +        if (!strstr(system_path_old, path)) { +            guard_free(system_path_new); +            return 0; +        } +    } +    if (setenv(key, system_path_new, 1) < 0) { +        SYSERROR("Unable to %s to PATH: %s", mode & PM_APPEND ? "append" : "prepend", path); +        guard_free(system_path_new); +        return -1; +    } + +    guard_free(system_path_new); +    return 0; +} + | 
