jaulib v1.3.0
Jau Support Library (C++, Java, ..)
os_support.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020-2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <cstdint>
26#include <cinttypes>
27#include <cstring>
28
29#include <ctime>
30
31#include <algorithm>
32#include <jau/cpuid.hpp>
33#include <jau/string_util.hpp>
34
35#include <jau/debug.hpp>
36#include <jau/file_util.hpp>
37#include <jau/os/dyn_linker.hpp>
38#include <jau/os/os_support.hpp>
39#include <jau/os/user_info.hpp>
40
41#if !defined(_WIN32)
42 #include <sys/utsname.h>
43#endif
44
45using namespace jau;
46
47#define CASE_TO_STRING(U,V) case U::V: return #V;
48
49#define OSTYPE_ENUM(X) \
50 X(os_type_t,Unix) \
51 X(os_type_t,Windows) \
52 X(os_type_t,Linux) \
53 X(os_type_t,Android) \
54 X(os_type_t,FreeBSD) \
55 X(os_type_t,Darwin) \
56 X(os_type_t,QnxNTO) \
57 X(os_type_t,GenWasm) \
58 X(os_type_t,Emscripten)
59
60std::string jau::os::to_string(const os_type_t v) noexcept {
61 switch(v) {
63 default: ; // fall through intended
64 }
65 return "undef";
66}
67
68
70 #if !defined(_WIN32)
71 struct utsname uinfo;
72 if( 0 == ::uname(&uinfo) ) {
73 info.sysname = std::string(uinfo.sysname);
74 info.nodename = std::string(uinfo.nodename);
75 info.release = std::string(uinfo.release);
76 info.version = std::string(uinfo.version);
77 info.machine = std::string(uinfo.machine);
78 #if defined(_GNU_SOURCE)
79 info.domainname = std::string(uinfo.domainname);
80 #endif
81 return true;
82 }
83 #endif
84 return false;
85}
86
87#define ABITYPE_ENUM(X) \
88 X(abi_type_t,generic) \
89 X(abi_type_t,gnu_armel) \
90 X(abi_type_t,gnu_armhf) \
91 X(abi_type_t,aarch64) \
92 X(abi_type_t,wasm32_gen) \
93 X(abi_type_t,wasm32_ems) \
94 X(abi_type_t,wasm64_gen) \
95 X(abi_type_t,wasm64_ems)
96
97std::string jau::os::to_string(const abi_type_t v) noexcept {
98 switch(v) {
100 default: ; // fall through intended
101 }
102 return "undef";
103}
104
105std::string jau::os::get_os_and_arch(const os_type_t os, const jau::cpu::cpu_family_t cpu, const abi_type_t abi, const endian_t e) noexcept {
106 std::string os_;
107 std::string _and_arch_tmp, _and_arch_final;
108
109 switch( cpu ) {
111 if( abi_type_t::gnu_armhf == abi ) {
112 _and_arch_tmp = "armv6hf";
113 } else {
114 _and_arch_tmp = "armv6";
115 }
116 break;
118 _and_arch_tmp = "i586";
119 break;
121 _and_arch_tmp = "ppc";
122 break;
124 _and_arch_tmp = jau::is_little_endian(e) ? "mipsel" : "mips";
125 break;
127 _and_arch_tmp = "sparc";
128 break;
130 _and_arch_tmp = "superh";
131 break;
132
134 _and_arch_tmp = "aarch64";
135 break;
137 _and_arch_tmp = "amd64";
138 break;
140 _and_arch_tmp = jau::is_little_endian(e) ? "ppc64le" : "ppc64";
141 break;
143 _and_arch_tmp = "mips64";
144 break;
146 _and_arch_tmp = "ia64";
147 break;
149 _and_arch_tmp = "sparcv9";
150 break;
152 _and_arch_tmp = "superh64";
153 break;
155 _and_arch_tmp = "wasm32";
156 break;
158 _and_arch_tmp = "wasm64";
159 break;
160 default:
161 _and_arch_tmp = "undef_arch";
162 break;
163 }
164
165 switch( os ) {
166 case os_type_t::Android:
167 os_ = "android";
168 _and_arch_final = _and_arch_tmp;
169 break;
170 case os_type_t::Darwin:
171 os_ = "darwin";
172 _and_arch_final = "universal";
173 break;
174 case os_type_t::Windows:
175 os_ = "windows";
176 _and_arch_final = _and_arch_tmp;
177 break;
178 case os_type_t::Linux:
179 os_ = "linux";
180 _and_arch_final = _and_arch_tmp;
181 break;
182 case os_type_t::FreeBSD:
183 os_ = "freebsd";
184 _and_arch_final = _and_arch_tmp;
185 break;
186 case os_type_t::QnxNTO:
187 os_ = "qnxnto";
188 _and_arch_final = _and_arch_tmp;
189 break;
190 case os_type_t::GenWasm:
191 os_ = "webasm";
192 _and_arch_final = _and_arch_tmp;
193 break;
194 case os_type_t::Emscripten:
195 os_ = "emscripten";
196 _and_arch_final = _and_arch_tmp;
197 break;
198 default:
199 os_ = "undef_os";
200 _and_arch_final = _and_arch_tmp;
201 break;
202 }
203 return os_ + "-" + _and_arch_final;
204}
205
206
207std::string jau::os::get_platform_info(std::string& sb) noexcept {
211 bool rti_ok = jau::os::get_rt_os_info(rti);
213
214 sb.append( jau::format_string("Platform: %s %s, %s (",
215 jau::os::to_string(os).c_str(),
216 ( rti_ok ? rti.release.c_str() : ""),
217 jau::cpu::to_string(cpu.family).c_str() ) );
218 cpu.toString(sb, true);
219 sb.append( jau::format_string("), abi %s, ", jau::os::to_string(abi).c_str()) );
220 sb.append( jau::os::get_os_and_arch(os, cpu.family, abi, cpu.byte_order) );
221 if( rti_ok ) {
222 sb.append(", runtime: ").append(rti.to_string()).append("\n");
223 }
224 return sb;
225}
226
227static
228std::pair<std::string, bool>
229DynamicLinker_processCanonicalNameImpl(bool strip, const std::string& filename, const bool isBasename,
230 const bool caseInsensitive=jau::os::is_windows()) noexcept
231{
232 const std::string prefix = jau::os::DynamicLinker::getDefaultPrefix();
233 const std::string suffix = jau::os::DynamicLinker::getDefaultSuffix();
234 const std::string libBaseName = isBasename ? filename : jau::fs::basename(filename);
235 const std::string libBaseNameLC = caseInsensitive ? jau::toLower(libBaseName) : libBaseName;
236 const size_t pre_idx = libBaseNameLC.find(prefix);
237 if( 0 == pre_idx ) { // starts-with
238 const size_t sfx_idx = libBaseNameLC.rfind(suffix);
239 if( std::string::npos != sfx_idx ) {
240 // Check to see if everything after it is a Unix version number
241 bool ok = true;
242 for (size_t i = sfx_idx + suffix.length(); i < libBaseName.size(); i++) {
243 const char c = libBaseName[i];
244 if (!(c == '.' || (c >= '0' && c <= '9'))) {
245 ok = false;
246 break;
247 }
248 }
249 if (ok) {
250 std::string res;
251 if( strip ) {
252 const size_t sfx_len = libBaseName.size() - sfx_idx;
253 res = libBaseName.substr(prefix.size(), libBaseName.size() - prefix.size() - sfx_len);
254 }
255 return std::make_pair(res, true);
256 }
257 }
258 }
259 return std::make_pair(std::string(), false);
260}
261
262std::string jau::os::DynamicLinker::getBaseName(const std::string& filename, const bool isBasename, const bool caseInsensitive) noexcept {
263 return DynamicLinker_processCanonicalNameImpl(true, filename, isBasename, caseInsensitive).first;
264}
265
266bool jau::os::DynamicLinker::isCanonicalName(const std::string& filename, const bool isBasename, const bool caseInsensitive) noexcept {
267 return DynamicLinker_processCanonicalNameImpl(false, filename, isBasename, caseInsensitive).second;
268}
269
270static std::vector<std::string> DynamicLinker_buildNames(const std::string& libName) noexcept {
271 std::vector<std::string> res;
272
273 const std::string libBaseName = jau::fs::basename(libName);
274 if( jau::os::DynamicLinker::isCanonicalName(libBaseName, true) ) {
275 // basename is canonical, so use the original with leading path
276 res.push_back(libName);
277 return res;
278 }
279
280 res.push_back( jau::os::DynamicLinker::getCanonicalName(libBaseName, false) );
281 if ( jau::os::is_darwin() ) {
282 // Plain library-base-name in Framework folder
283 res.push_back(libBaseName);
284 }
285 return res;
286}
287
288static void DynamicLinker_addBasenames(const std::string& cause, const std::vector<std::string>& baseNames, std::vector<std::string>& paths) noexcept {
289 for (const std::string& baseName : baseNames) {
290 DBG_PRINT("NativeLibrary.enumerateLibraryPaths: %s: '%s'", cause.c_str(), baseName.c_str());
291 paths.push_back(baseName);
292 }
293}
294static void DynamicLinker_addAbsPaths(const std::string& cause, const std::string& abs_path, const std::vector<std::string>& baseNames, std::vector<std::string>& paths) noexcept {
295 for (const std::string& baseName : baseNames) {
296 std::string p(abs_path); p.append("/").append(baseName);
297 DBG_PRINT("NativeLibrary.enumerateLibraryPaths: %s: '%s', from path '%s'", cause.c_str(), p.c_str(), abs_path.c_str());
298 paths.push_back(p);
299 }
300}
301static void DynamicLinker_addSysPaths(const std::string& cause, const std::vector<std::string>& baseNames, std::vector<std::string>& paths) noexcept {
302 // First add just the library names to use the OS's search algorithm
303 DynamicLinker_addBasenames(cause, baseNames, paths);
304
305 // Second add full path for each sys-folder to overcome SONAME mismatch (OS's search algorithm)
306 std::vector<std::string> lib_paths = jau::os::DynamicLinker::getSystemEnvLibraryPaths();
307 for(const std::string& p : lib_paths) {
308 DynamicLinker_addAbsPaths(cause, p, baseNames, paths);
309 }
310}
311#if 0
312static void DynamicLinker_addRelPaths(const std::string& cause, const std::string& path, const std::vector<std::string>& baseNames, std::vector<std::string>& paths) noexcept {
313 std::string abs_path;
314 jau::fs::file_stats path_stats(path);
315 if( path_stats.exists() ) {
316 DynamicLinker_addAbsPaths(cause, path_stats.path(), baseNames, paths);
317 }
318}
319#endif
320
321std::vector<std::string> jau::os::DynamicLinker::enumerateLibraryPaths(const std::string& libName,
322 bool searchSystemPath,
323 bool searchSystemPathFirst) noexcept {
324 DBG_PRINT("DynamicLibrary.enumerateLibraryPaths: libName '%s'", libName.c_str());
325 std::vector<std::string> paths;
326 if ( 0 == libName.size() ) {
327 return paths;
328 }
329
330 // Allow user's full path specification to override our building of paths
331 if ( jau::fs::isAbsolute(libName) ) {
332 paths.push_back(libName);
333 DBG_PRINT("NativeLibrary.enumerateLibraryPaths: done, absolute path found '%s'", libName.c_str());
334 return paths;
335 }
336
337 std::vector<std::string> baseNames = DynamicLinker_buildNames(libName);
338 DBG_PRINT("NativeLibrary.enumerateLibraryPaths: baseNames: %s", jau::to_string(baseNames).c_str());
339
340 if( searchSystemPath && searchSystemPathFirst ) {
341 DynamicLinker_addSysPaths("add.ssp_1st", baseNames, paths);
342
343 // Add probable Mac OS X-specific paths
344 if ( jau::os::is_darwin() ) {
345 // Add historical location
346 DynamicLinker_addAbsPaths("add.ssp_1st_macos_old", "/Library/Frameworks/" + libName + ".framework", baseNames, paths);
347 // Add current location
348 DynamicLinker_addAbsPaths("add.ssp_1st_macos_cur", "/System/Library/Frameworks/" + libName + ".framework", baseNames, paths);
349 }
350 }
351
352 // Add current working directory
353 {
354 std::string cwd = jau::fs::get_cwd();
355 DynamicLinker_addAbsPaths("add.cwd", cwd, baseNames, paths);
356
357 // Add current working directory + natives/os-arch/ + library names (for unpacked archives, if exists)
358 const std::string cwd_bin = cwd+"/natives/"+jau::os::get_os_and_arch();
359 jau::fs::file_stats fstats(cwd_bin);
360 if( fstats.exists() ) {
361 DynamicLinker_addAbsPaths("add.cwd.natives.os_arch", cwd_bin, baseNames, paths);
362 }
363 }
364
365 // Add user directory
366 {
368 if( user.isValid() ) {
369 DynamicLinker_addAbsPaths("add.home.std", user.homedir(), baseNames, paths);
370
371 // Add current home + bin/os-arch/ + library names (if exists)
372 const std::string home_bin = user.homedir()+"/bin/"+jau::os::get_os_and_arch();
373 jau::fs::file_stats fstats(home_bin);
374 if( fstats.exists() ) {
375 DynamicLinker_addAbsPaths("add.home.bin.os_arch", home_bin, baseNames, paths);
376 }
377 }
378 }
379
380 if( searchSystemPath && !searchSystemPathFirst ) {
381 DynamicLinker_addSysPaths("add.ssp_lst", baseNames, paths);
382
383 // Add probable Mac OS X-specific paths
384 if ( jau::os::is_darwin() ) {
385 // Add historical location
386 DynamicLinker_addAbsPaths("add.ssp_lst_macos_old", "/Library/Frameworks/" + libName + ".framework", baseNames, paths);
387 // Add current location
388 DynamicLinker_addAbsPaths("add.ssp_lst_macos_cur", "/System/Library/Frameworks/" + libName + ".framework", baseNames, paths);
389 }
390 }
391 DBG_PRINT("NativeLibrary.enumerateLibraryPaths: done: %s", jau::to_string(paths).c_str());
392 return paths;
393}
394
Singleton CpuInfo caching all jau::cpu information.
Definition: cpuid.hpp:316
static const CpuInfo & get() noexcept
Returns reference to const singleton instance.
Definition: cpuid.hpp:356
cpu_family_t family
cpu_family_t derived from Architectures predefined compiler macros.
Definition: cpuid.hpp:335
std::string toString(std::string &sb, bool details_only=false) const noexcept
Definition: cpuid.cpp:393
jau::endian_t byte_order
Definition: cpuid.hpp:336
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
Definition: file_util.hpp:406
constexpr bool exists() const noexcept
Returns true if entity does not exist, exclusive bit.
Definition: file_util.hpp:648
static std::string getBaseName(const std::string &filename, const bool isBasename=false, const bool caseInsensitive=jau::os::is_windows()) noexcept
Returns the library basename, i.e.
Definition: os_support.cpp:262
static std::vector< std::string > enumerateLibraryPaths(const std::string &libName, bool searchSystemPath=false, bool searchSystemPathFirst=false) noexcept
Returns list of potential absolute library filenames.
Definition: os_support.cpp:321
static constexpr_cxx20 std::string getDefaultPrefix() noexcept
Returns the native library prefix, e.g.
Definition: dyn_linker.hpp:160
static constexpr_cxx20 std::string getDefaultSuffix() noexcept
Returns the native library suffix including the dot, e.g.
Definition: dyn_linker.hpp:168
static constexpr_cxx20 std::string getCanonicalName(const std::string &basename, const bool checkIsCanonical=true) noexcept
Returns canonical library name for this system from given library-basename, e.g.
Definition: dyn_linker.hpp:186
static bool isCanonicalName(const std::string &filename, const bool isBasename=false, const bool caseInsensitive=jau::os::is_windows()) noexcept
Returns true if the given filename contains the canonical prefix and suffix, otherwise returns false.
Definition: os_support.cpp:266
static std::vector< std::string > getSystemEnvLibraryPaths()
Returns a list of system paths, from the getSystemEnvLibraryPathVarname() variable.
Definition: dyn_linker.hpp:155
User account information of the underlying OS.
Definition: user_info.hpp:42
const std::string & homedir() const noexcept
Definition: user_info.hpp:81
bool isValid() const noexcept
Definition: user_info.hpp:77
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
endian_t
Endian identifier, indicating endianess of all scalar types.
Definition: byte_util.hpp:203
std::string to_string(const endian_t v) noexcept
Return std::string representation of the given endian.
constexpr bool is_little_endian(const endian_t byte_order) noexcept
Returns true if given byte_order equals endian::little, otherwise false.
Definition: byte_util.hpp:293
std::string basename(const std::string_view &path) noexcept
Return stripped leading directory components from given path separated by /.
Definition: file_util.cpp:151
std::string get_cwd() noexcept
Return the current working directory or empty on failure.
Definition: file_util.cpp:82
bool isAbsolute(const std::string_view &path) noexcept
Returns true if first character is / or - in case of Windows - \\.
Definition: file_util.cpp:172
bool get_rt_os_info(RuntimeOSInfo &info) noexcept
Definition: os_support.cpp:69
constexpr bool is_darwin() noexcept
Evaluates true if platform os_type::native contains os_type::Darwin.
Definition: os_support.hpp:212
os_type_t
OS type bits and unique IDs.
Definition: os_support.hpp:99
constexpr abi_type_t get_abi_type(const jau::cpu::cpu_family_t cpu) noexcept
Definition: os_support.hpp:261
std::string get_os_and_arch(const os_type_t os, const jau::cpu::cpu_family_t cpu, const abi_type_t abi, const endian_t e) noexcept
Returns the common name for the given os_type, jau::cpu::cpu_family, abi_type and endian.
Definition: os_support.cpp:105
constexpr bool is_windows() noexcept
Evaluates true if platform os_type::native contains os_type::Windows.
Definition: os_support.hpp:200
std::string to_string(const os_type_t mask) noexcept
Return the string representation of os_type.
Definition: os_support.cpp:60
std::string get_platform_info(std::string &sb) noexcept
Definition: os_support.cpp:207
@ native
Identifier for native OS type, one of the above.
std::string format_string(const char *format,...) noexcept
Returns a string according to printf() formatting rules and variable number of arguments following th...
std::string toLower(const std::string &s) noexcept
std::string to_string(const cpu_family_t v) noexcept
Definition: cpuid.cpp:149
cpu_family_t
Definition: cpuid.hpp:47
@ x86_64
AMD/Intel 64-bit.
@ x86_32
AMD/Intel 32-bit.
@ sparc32
SPARC 32bit.
@ wasm64
WebAssembly 64-bit.
@ superh32
Hitachi SuperH 32bit.
@ superh64
Hitachi SuperH 64bit.
@ wasm32
WebAssembly 32-bit.
@ ppc64
Power PC 32bit.
@ sparc64
SPARC 32bit.
@ ppc32
Power PC 32bit.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
static std::vector< std::string > DynamicLinker_buildNames(const std::string &libName) noexcept
Definition: os_support.cpp:270
#define OSTYPE_ENUM(X)
Definition: os_support.cpp:49
static void DynamicLinker_addAbsPaths(const std::string &cause, const std::string &abs_path, const std::vector< std::string > &baseNames, std::vector< std::string > &paths) noexcept
Definition: os_support.cpp:294
#define CASE_TO_STRING(U, V)
Definition: os_support.cpp:47
#define ABITYPE_ENUM(X)
Definition: os_support.cpp:87
static void DynamicLinker_addBasenames(const std::string &cause, const std::vector< std::string > &baseNames, std::vector< std::string > &paths) noexcept
Definition: os_support.cpp:288
static void DynamicLinker_addSysPaths(const std::string &cause, const std::vector< std::string > &baseNames, std::vector< std::string > &paths) noexcept
Definition: os_support.cpp:301
static std::pair< std::string, bool > DynamicLinker_processCanonicalNameImpl(bool strip, const std::string &filename, const bool isBasename, const bool caseInsensitive=jau::os::is_windows()) noexcept
Definition: os_support.cpp:229
std::string to_string() noexcept
Definition: os_support.hpp:230