Skip to content

Commit 027bb55

Browse files
fix: resolve executable path when run via PATH
When clice is executed via PATH (e.g., 'clice' instead of '/path/to/clice'), resolve_self_path() incorrectly resolves the path relative to current directory instead of the actual executable location. This causes worker pool to fail with 'no such file or directory' error. Add search_in_path() function to search PATH environment variable when argv[0] contains no path separators. Fixes: worker pool fails to start when clice is in PATH
1 parent ce2f355 commit 027bb55

File tree

1 file changed

+64
-4
lines changed

1 file changed

+64
-4
lines changed

src/clice.cc

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,77 @@ struct CliOptions {
8787
DECO_CFG_END();
8888
};
8989

90+
auto search_in_path(std::string_view name) -> std::string {
91+
const char* path_env = std::getenv("PATH");
92+
if(!path_env || name.empty()) {
93+
return std::string(name);
94+
}
95+
96+
#ifdef _WIN32
97+
constexpr char path_sep = ';';
98+
constexpr auto is_executable = [](const std::filesystem::path& p) {
99+
return std::filesystem::exists(p) && !std::filesystem::is_directory(p);
100+
};
101+
#else
102+
constexpr char path_sep = ':';
103+
constexpr auto is_executable = [](const std::filesystem::path& p) {
104+
std::error_code ec;
105+
auto status = std::filesystem::status(p, ec);
106+
if(ec || !std::filesystem::exists(status) || std::filesystem::is_directory(status)) {
107+
return false;
108+
}
109+
return (status.permissions() & (std::filesystem::perms::owner_exec |
110+
std::filesystem::perms::group_exec |
111+
std::filesystem::perms::others_exec)) != std::filesystem::perms::none;
112+
};
113+
#endif
114+
115+
std::string_view path_view(path_env);
116+
size_t start = 0;
117+
while(start < path_view.size()) {
118+
size_t end = path_view.find(path_sep, start);
119+
if(end == std::string_view::npos) {
120+
end = path_view.size();
121+
}
122+
123+
std::string_view dir = path_view.substr(start, end - start);
124+
if(!dir.empty()) {
125+
std::filesystem::path full_path = std::filesystem::path(dir) / name;
126+
std::error_code ec;
127+
auto canonical = std::filesystem::canonical(full_path, ec);
128+
if(!ec && is_executable(canonical)) {
129+
return canonical.string();
130+
}
131+
}
132+
133+
start = end + 1;
134+
}
135+
136+
return std::string(name);
137+
}
138+
90139
auto resolve_self_path(int argc, const char** argv) -> std::string {
91140
if(argc <= 0 || argv == nullptr || argv[0] == nullptr) {
92141
return "clice";
93142
}
94143

144+
std::string_view arg0(argv[0]);
95145
std::error_code ec;
96-
auto absolute = std::filesystem::absolute(argv[0], ec);
97-
if(ec) {
98-
return std::string(argv[0]);
146+
147+
// If arg0 contains a path separator, treat it as a path (relative or absolute)
148+
if(arg0.find('/') != std::string_view::npos || arg0.find('\\') != std::string_view::npos) {
149+
auto absolute = std::filesystem::absolute(arg0, ec);
150+
if(!ec) {
151+
auto canonical = std::filesystem::weakly_canonical(absolute, ec);
152+
if(!ec) {
153+
return canonical.string();
154+
}
155+
}
156+
return std::string(arg0);
99157
}
100-
return absolute.string();
158+
159+
// No path separator - search in PATH
160+
return search_in_path(arg0);
101161
}
102162

103163
auto build_options(const CliOptions& cli_options, int argc, const char** argv)

0 commit comments

Comments
 (0)