1 /** @file
2
3 runroot.cc
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /*
25 Please refer to traffic_layout document for the usage
26 */
27
28 #include "tscore/ink_error.h"
29 #include "tscore/I_Layout.h"
30 #include "tscore/runroot.h"
31 #include <yaml-cpp/yaml.h>
32
33 static std::string runroot_file = {};
34
35 // this is a temporary approach and will be replaced when std::filesystem is in
36 bool
exists(const std::string & dir)37 exists(const std::string &dir)
38 {
39 struct stat buffer;
40 int result = stat(dir.c_str(), &buffer);
41 return (!result) ? true : false;
42 }
43
44 bool
is_directory(const std::string & directory)45 is_directory(const std::string &directory)
46 {
47 struct stat buffer;
48 int result = stat(directory.c_str(), &buffer);
49 return (!result && (S_IFDIR & buffer.st_mode)) ? true : false;
50 }
51
52 // the function for the checking of the yaml file in the passed in path
53 // if found return the path to the yaml file, if not return empty string.
54 static std::string
get_yaml_path(const std::string & path)55 get_yaml_path(const std::string &path)
56 {
57 // yaml_file 2 and 3 are for temporary use in case the change may break for people using runroot already
58 // this can be removed in the future.
59 std::string yaml_file;
60 std::string yaml_file2;
61 std::string yaml_file3;
62 if (is_directory(path.c_str())) {
63 std::string yaml_file(Layout::relative_to(path, "runroot.yaml"));
64 if (exists(yaml_file)) {
65 return yaml_file;
66 }
67 std::string yaml_file2(Layout::relative_to(path, "runroot.yml"));
68 if (exists(yaml_file2)) {
69 return yaml_file2;
70 }
71 std::string yaml_file3(Layout::relative_to(path, "runroot_path.yml"));
72 if (exists(yaml_file3)) {
73 return yaml_file3;
74 }
75 } else if (exists(path)) {
76 return path;
77 }
78 return {};
79 }
80
81 // the function for the checking of the yaml file in passed in directory or parent directory
82 // if found return the parent path to the yaml file
83 static std::string
get_parent_yaml_path(const std::string & path)84 get_parent_yaml_path(const std::string &path)
85 {
86 std::string whole_path = path;
87 if (whole_path.back() == '/') {
88 whole_path.pop_back();
89 }
90 // go up to 4 level of parent directories
91 for (int i = 0; i < 4; i++) {
92 if (whole_path.empty()) {
93 return {};
94 }
95 std::string yaml_file = get_yaml_path(whole_path);
96 if (!yaml_file.empty()) {
97 return yaml_file;
98 }
99 whole_path = whole_path.substr(0, whole_path.find_last_of('/'));
100 }
101 return {};
102 }
103
104 static void
runroot_extra_handling(const char * executable,bool json)105 runroot_extra_handling(const char *executable, bool json)
106 {
107 std::string path;
108 // 2. check Environment variable
109 char *env_val = getenv("TS_RUNROOT");
110 if (env_val) {
111 path = get_yaml_path(env_val);
112 if (!path.empty()) {
113 runroot_file = path;
114 if (!json) {
115 ink_notice("using the environment variable TS_RUNROOT");
116 }
117 return;
118 } else if (!json) {
119 ink_warning("Unable to access runroot: '%s' from $TS_RUNROOT", env_val);
120 }
121 }
122 // 3. find cwd or parent path of cwd to check
123 char cwd[PATH_MAX] = {0};
124 if (getcwd(cwd, sizeof(cwd)) != nullptr) {
125 path = get_parent_yaml_path(cwd);
126 if (!path.empty()) {
127 runroot_file = path;
128 if (!json) {
129 ink_notice("using cwd as TS_RUNROOT");
130 }
131 return;
132 }
133 }
134 // 4. installed executable
135 char RealBinPath[PATH_MAX] = {0};
136 if ((executable != nullptr) && realpath(executable, RealBinPath) != nullptr) {
137 std::string bindir = RealBinPath;
138 bindir = bindir.substr(0, bindir.find_last_of('/')); // getting the bin dir not executable path
139 path = get_parent_yaml_path(bindir);
140 if (!path.empty()) {
141 runroot_file = path;
142 if (!json) {
143 ink_notice("using the installed dir as TS_RUNROOT");
144 }
145 return;
146 }
147 }
148 // 5. if no runroot use, using default build
149 return;
150 }
151
152 // This is a temporary approach to handle runroot with migration to ArgParser.
153 // When all program use ArgParser, we can remove the runroot_handler below and replace it with this one.
154 void
argparser_runroot_handler(std::string const & value,const char * executable,bool json)155 argparser_runroot_handler(std::string const &value, const char *executable, bool json)
156 {
157 // 1.if --run-root is provided
158 if (!value.empty()) {
159 std::string path = get_yaml_path(value);
160 if (!path.empty()) {
161 if (!json) {
162 ink_notice("using command line path as RUNROOT");
163 }
164 runroot_file = path;
165 return;
166 } else if (!json) {
167 ink_warning("Unable to access runroot: '%s'", value.c_str());
168 }
169 }
170 runroot_extra_handling(executable, json);
171 }
172
173 // handler for ts runroot
174 // this function set up runroot_file
175 void
runroot_handler(const char ** argv,bool json)176 runroot_handler(const char **argv, bool json)
177 {
178 std::string prefix = "--run-root";
179 std::string path;
180
181 // check if we have --run-root...
182 std::string arg = {};
183
184 int i = 0;
185 while (argv[i]) {
186 std::string_view command = argv[i];
187 if (command.substr(0, prefix.size()) == prefix) {
188 arg = command.data();
189 break;
190 }
191 i++;
192 }
193
194 // if --run-root is provided
195 if (!arg.empty() && arg != prefix) {
196 // 1. pass in path
197 prefix += "=";
198 std::string value = arg.substr(prefix.size(), arg.size() - 1);
199 path = get_yaml_path(value);
200 if (!path.empty()) {
201 if (!json) {
202 ink_notice("using command line path as RUNROOT");
203 }
204 runroot_file = path;
205 return;
206 } else if (!json) {
207 ink_warning("Unable to access runroot: '%s'", value.c_str());
208 }
209 }
210
211 runroot_extra_handling(argv[0], json);
212 }
213
214 // return a map of all path with default layout
215 std::unordered_map<std::string, std::string>
runroot_map_default()216 runroot_map_default()
217 {
218 std::unordered_map<std::string, std::string> map;
219
220 map[LAYOUT_PREFIX] = Layout::get()->prefix;
221 map[LAYOUT_EXEC_PREFIX] = Layout::get()->exec_prefix;
222 map[LAYOUT_BINDIR] = Layout::get()->bindir;
223 map[LAYOUT_SBINDIR] = Layout::get()->sbindir;
224 map[LAYOUT_SYSCONFDIR] = Layout::get()->sysconfdir;
225 map[LAYOUT_DATADIR] = Layout::get()->datadir;
226 map[LAYOUT_INCLUDEDIR] = Layout::get()->includedir;
227 map[LAYOUT_LIBDIR] = Layout::get()->libdir;
228 map[LAYOUT_LIBEXECDIR] = Layout::get()->libexecdir;
229 map[LAYOUT_LOCALSTATEDIR] = Layout::get()->localstatedir;
230 map[LAYOUT_RUNTIMEDIR] = Layout::get()->runtimedir;
231 map[LAYOUT_LOGDIR] = Layout::get()->logdir;
232 // mandir is not needed for runroot
233 map[LAYOUT_CACHEDIR] = Layout::get()->cachedir;
234
235 return map;
236 }
237
238 // return a map of all path in runroot.yaml
239 RunrootMapType
runroot_map(const std::string & file)240 runroot_map(const std::string &file)
241 {
242 RunrootMapType map;
243 try {
244 YAML::Node yamlfile = YAML::LoadFile(file);
245 std::string prefix = file.substr(0, file.find_last_of('/'));
246
247 for (const auto &it : yamlfile) {
248 // key value pairs of dirs
249 std::string value = it.second.as<std::string>();
250 if (value[0] != '/') {
251 value = Layout::relative_to(prefix, value);
252 }
253 map[it.first.as<std::string>()] = value;
254 }
255 } catch (YAML::Exception &e) {
256 ink_warning("Unable to read '%s': %s", file.c_str(), e.what());
257 ink_notice("Continuing with default value");
258 return RunrootMapType{};
259 }
260 return map;
261 }
262
263 // check for the using of runroot
264 // a map of all path will be returned
265 // if we do not use runroot, a empty map will be returned.
266 RunrootMapType
check_runroot()267 check_runroot()
268 {
269 if (runroot_file.empty()) {
270 return RunrootMapType{};
271 }
272
273 int len = runroot_file.size();
274 if ((len + 1) > PATH_NAME_MAX) {
275 ink_fatal("runroot path is too big: %d, max %d\n", len, PATH_NAME_MAX - 1);
276 }
277 return runroot_map(runroot_file);
278 }
279
280 std::string_view
get_runroot()281 get_runroot()
282 {
283 return runroot_file;
284 }
285