busybox_info.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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. #ifndef CARBON_TOOLCHAIN_INSTALL_BUSYBOX_INFO_H_
  5. #define CARBON_TOOLCHAIN_INSTALL_BUSYBOX_INFO_H_
  6. #include <filesystem>
  7. #include <iterator>
  8. #include <optional>
  9. #include <string>
  10. #include "common/error.h"
  11. #include "llvm/ADT/StringRef.h"
  12. namespace Carbon {
  13. constexpr const char* Argv0OverrideEnv = "CARBON_ARGV0_OVERRIDE";
  14. struct BusyboxInfo {
  15. // The path to `carbon-busybox`.
  16. std::filesystem::path bin_path;
  17. // The mode, such as `carbon` or `clang`.
  18. std::optional<std::string> mode;
  19. };
  20. // Returns the busybox information, given argv[0].
  21. //
  22. // Extracts the desired mode for the busybox from the initial command name.
  23. //
  24. // Checks if the path in argv0 is an executable in a valid Carbon install, or a
  25. // symlink to such an executable, and sets `bin_path` to the path of
  26. // `lib/carbon/carbon-busybox` within that install.
  27. //
  28. // If unable to locate a plausible busybox binary, returns an error instead.
  29. inline auto GetBusyboxInfo(llvm::StringRef argv0) -> ErrorOr<BusyboxInfo> {
  30. // Check for an override of `argv[0]` from the environment and apply it.
  31. if (const char* argv0_override = getenv(Argv0OverrideEnv)) {
  32. argv0 = argv0_override;
  33. }
  34. BusyboxInfo info = {.bin_path = argv0.str(), .mode = std::nullopt};
  35. std::filesystem::path filename = info.bin_path.filename();
  36. // The mode is set to the initial filename used for `argv[0]`.
  37. if (filename != "carbon" && filename != "carbon-busybox") {
  38. info.mode = filename;
  39. }
  40. // Now search through any symlinks to locate the installed busybox binary.
  41. while (true) {
  42. filename = info.bin_path.filename();
  43. if (filename == "carbon-busybox") {
  44. return info;
  45. }
  46. // If we've not already reached the busybox, look for it relative to the
  47. // current binary path. This can help more immediately locate an
  48. // installation tree, and avoids walking through a final layer of symlinks
  49. // which may point to content-addressed storage or other parts of a build
  50. // output tree.
  51. //
  52. // We break this into two cases we need to handle:
  53. // - Carbon's CLI will be: `<prefix>/bin/carbon`
  54. // - Other tools will be: `<prefix>/lib/carbon/<group>/bin/<tool>`
  55. //
  56. // We also check that the current path is within a `bin` directory to
  57. // provide best-effort checking for accidentally walking up from symlinks
  58. // that aren't within an installation-shaped tree.
  59. auto parent_path = info.bin_path.parent_path();
  60. // Strip any `.` path components at the end to simplify processing.
  61. while (parent_path.filename() == ".") {
  62. parent_path = parent_path.parent_path();
  63. }
  64. if (parent_path.filename() == "bin") {
  65. auto lib_path = filename == "carbon"
  66. ? parent_path / ".." / "lib" / "carbon"
  67. : parent_path / ".." / "..";
  68. auto busybox_path = lib_path / "carbon-busybox";
  69. std::error_code ec;
  70. if (std::filesystem::exists(busybox_path, ec)) {
  71. info.bin_path = busybox_path;
  72. return info;
  73. }
  74. }
  75. // Try to walk through another layer of symlinks and see if we can find the
  76. // installation there or are linked directly to the busybox.
  77. std::error_code ec;
  78. auto symlink_target = std::filesystem::read_symlink(info.bin_path, ec);
  79. if (ec) {
  80. return ErrorBuilder()
  81. << "expected carbon-busybox symlink at `" << info.bin_path << "`";
  82. }
  83. // Do a path join, to handle relative symlinks.
  84. info.bin_path = parent_path / symlink_target;
  85. }
  86. }
  87. } // namespace Carbon
  88. #endif // CARBON_TOOLCHAIN_INSTALL_BUSYBOX_INFO_H_