busybox_info.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/install/busybox_info.h"
  5. #include <iterator>
  6. #include "common/exe_path.h"
  7. #include "common/filesystem.h"
  8. #include "llvm/ADT/StringRef.h"
  9. namespace Carbon {
  10. // The mode is set to the initial filename used for `argv[0]`.
  11. static auto GetMode(const std::filesystem::path& argv0)
  12. -> std::optional<std::string> {
  13. std::string filename = argv0.filename();
  14. if (filename != "carbon" && filename != "carbon-busybox") {
  15. return filename;
  16. }
  17. return std::nullopt;
  18. }
  19. // Try to walk up the path using `.parent_path()` if we can to avoid extra
  20. // components to resolve. However, if the path is relative to the current
  21. // working directory and we run out of parent components, walk up by appending
  22. // `../` components instead.
  23. static auto WalkUp(std::filesystem::path p) -> std::filesystem::path {
  24. // Remove `./` components.
  25. while (p.filename() == ".") {
  26. p = p.parent_path();
  27. }
  28. if (!p.is_absolute() && (p.empty() || p.filename() == "..")) {
  29. return p / "..";
  30. } else {
  31. return p.parent_path();
  32. }
  33. }
  34. auto GetBusyboxInfo(const char* argv0) -> ErrorOr<BusyboxInfo> {
  35. // Need storage due to `unsetenv` affecting `getenv` lifetime; using `path`
  36. // for `GetMode`.
  37. std::filesystem::path argv0_path = argv0;
  38. // Check for an override of `argv[0]` from the environment and apply it.
  39. if (const char* argv0_override = getenv(Argv0OverrideEnv)) {
  40. argv0_path = argv0_override;
  41. unsetenv(Argv0OverrideEnv);
  42. }
  43. BusyboxInfo info = {.bin_path = FindExecutablePath(argv0_path.c_str()),
  44. .mode = GetMode(argv0_path)};
  45. // Now search through any symlinks to locate the installed busybox binary.
  46. while (true) {
  47. if (info.bin_path.filename() == "carbon-busybox") {
  48. // Check for bazel structure. For example, this makes work:
  49. // /bin/sh -c "exec -a carbon ./bazel-bin/toolchain/carbon"
  50. // /bin/sh -c "exec -a llvm-symbolizer ./bazel-bin/toolchain/carbon"
  51. //
  52. // This will never occur in a "bin" subdirectory, so doesn't need to be
  53. // handled in the other return path.
  54. std::string busybox_path = info.bin_path.parent_path().string() +
  55. "/prefix/lib/carbon/carbon-busybox";
  56. if (auto access = Filesystem::Cwd().Access(busybox_path);
  57. access.ok() && *access) {
  58. info.bin_path = busybox_path;
  59. }
  60. return info;
  61. }
  62. // If we've not already reached the busybox, look for it relative to the
  63. // current binary path. This can help more immediately locate an
  64. // installation tree, and avoids walking through a final layer of symlinks
  65. // which may point to content-addressed storage or other parts of a build
  66. // output tree.
  67. //
  68. // We break this into two cases we need to handle:
  69. // - Carbon's CLI will be: `<prefix>/bin/carbon`
  70. // - Other tools will be: `<prefix>/lib/carbon/<group>/bin/<tool>`
  71. //
  72. // We also check that the current path is within a `bin` directory to
  73. // provide best-effort checking for accidentally walking up from symlinks
  74. // that aren't within an installation-shaped tree.
  75. auto parent_path = info.bin_path.parent_path();
  76. // Strip any `.` path components at the end to simplify processing.
  77. while (parent_path.filename() == ".") {
  78. parent_path = parent_path.parent_path();
  79. }
  80. if (parent_path.filename() == "bin") {
  81. // Note that we use a specialized approach to walking up rather than
  82. // always appending `../` components. While largely equivalent, this helps
  83. // keep paths shorter and avoids redundant work. We also don't expect to
  84. // need to respect _internally_ strange symlinking structures that would
  85. // need to use appended `../` components.
  86. auto lib_path = info.bin_path.filename() == "carbon"
  87. ? WalkUp(std::move(parent_path)) / "lib" / "carbon"
  88. : WalkUp(WalkUp(std::move(parent_path)));
  89. auto busybox_path = lib_path / "carbon-busybox";
  90. if (auto access = Filesystem::Cwd().Access(busybox_path);
  91. access.ok() && *access) {
  92. info.bin_path = busybox_path;
  93. return info;
  94. }
  95. }
  96. // Try to walk through another layer of symlinks and see if we can find the
  97. // installation there or are linked directly to the busybox.
  98. auto readlink = Filesystem::Cwd().Readlink(info.bin_path);
  99. if (!readlink.ok()) {
  100. return ErrorBuilder()
  101. << "expected carbon-busybox symlink at `" << info.bin_path << "`";
  102. }
  103. // Do a path join, to handle relative symlinks.
  104. info.bin_path = parent_path / *readlink;
  105. }
  106. }
  107. } // namespace Carbon