// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2018 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . #include #include #include #include #include #include #include #include using std::string; // normally configure would find this out // only activate one of the three to test with #define HAVE_SYS_UTSNAME_H 1 #define HAVE_SYS_SYSCTL_H 0 #define HAVE_SYS_SYSTEMINFO_H 0 #if HAVE_SYS_SYSCTL_H #include #endif #if HAVE_SYS_SYSTEMINFO_H #include #endif #if HAVE_SYS_UTSNAME_H #include #endif #define false 0 #define true 1 #define bool int #define strlcpy strncpy #define strlcat strncat #define safe_strcpy(x, y) strlcpy(x, y, sizeof(x)) #define safe_strcat(x, y) strlcat(x, y, sizeof(x)) #define LINUX_LIKE_SYSTEM (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(__HAIKU__) #if WASM #include #endif #if WASM EM_JS(FILE*, popen, (const char* command, const char* mode), { //TODO: add javascript code }); #endif enum LINUX_OS_INFO_PARSER { lsbrelease, osrelease, redhatrelease }; const char command_lsbrelease[] = "/usr/bin/lsb_release -a 2>&1"; const char file_osrelease[] = "/etc/os-release"; const char file_redhatrelease[] = "/etc/redhat-release"; // remove whitespace from start and end of a string // void strip_whitespace(string& str) { while (1) { if (str.length() == 0) break; if (!isascii(str[0])) break; if (!isspace(str[0])) break; str.erase(0, 1); } int n = (int) str.length(); while (n>0) { if (!isascii(str[n-1])) break; if (!isspace(str[n-1])) break; n--; } str.erase(n, str.length()-n); } void strip_whitespace(char *str) { string s = str; strip_whitespace(s); safe_strcpy(str, s.c_str()); } // remove whitespace and quotes from start and end of a string // void strip_quotes(string& str) { while (1) { if (str.length() == 0) break; if (str[0] == '"' || str[0] == '\'') { str.erase(0, 1); continue; } if (!isascii(str[0])) break; if (!isspace(str[0])) break; str.erase(0, 1); } int n = (int) str.length(); while (n>0) { if (str[n-1] == '"' || str[n-1] == '\'') { if (str[n-2] != '\\') { n--; continue; } } if (!isascii(str[n-1])) break; if (!isspace(str[n-1])) break; n--; } str.erase(n, str.length()-n); } void strip_quotes(char *str) { string s = str; strip_quotes(s); safe_strcpy(str, s.c_str()); } void unescape_os_release(char* buf) { char* out = buf; char* in = buf; while (*in) { if (*in != '\\') { *out++ = *in++; } else if (*(in+1) == '$') { *out++ = '$'; in += 2; } else if (*(in+1) == '\'') { *out++ = '\''; in += 2; } else if (*(in+1) == '"') { *out++ = '"'; in += 2; } else if (*(in+1) == '\\') { *out++ = '\\'; in += 2; } else if (*(in+1) == '`') { *out++ = '`'; in += 2; } else { *out++ = *in++; } } *out = 0; } int get_libc_version(string& version, string& extra_info) { char buf[1024] = ""; string strbuf; FILE* f = popen("PATH=/usr/bin:/bin:/usr/local/bin ldd --version 2>&1", "r"); if (f) { char* retval = fgets(buf, sizeof(buf), f); strbuf = (string)buf; while (fgets(buf, sizeof(buf), f)) { // consume output to allow command to exit gracefully } int status = pclose(f); if (!retval || status == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) { return 1; } strip_whitespace(strbuf); string::size_type parens1 = strbuf.find('('); string::size_type parens2 = strbuf.rfind(')'); string::size_type blank = strbuf.rfind(' '); if (blank != string::npos) { // extract version number version = strbuf.substr(blank+1); } else { return 1; } if (parens1 != string::npos && parens2 != string::npos && parens1 < parens2) { // extract extra information without parenthesis extra_info = strbuf.substr(parens1+1, parens2-parens1-1); } } else { return 1; } return 0; } std::vector split(std::string s, char delim) { std::vector result; std::stringstream ss(s); std::string item; while (getline(ss, item, delim)) { result.push_back(item); } return result; } bool parse_linux_os_info(const std::vector& lines, const LINUX_OS_INFO_PARSER parser, char* os_name, const int os_name_size, char* os_version, const int os_version_size) { if (lines.empty()) { return false; } bool found_something = false; char buf[256], buf2[256]; char dist_pretty[256], dist_name[256], dist_version[256], dist_codename[256]; //string os_version_extra(""); safe_strcpy(dist_pretty, ""); safe_strcpy(dist_name, ""); safe_strcpy(dist_version, ""); safe_strcpy(dist_codename, ""); switch (parser) { case lsbrelease: { for (unsigned int i = 0; i < lines.size(); ++i) { safe_strcpy(buf, lines[i].c_str()); strip_whitespace(buf); if (strstr(buf, "Description:")) { found_something = true; safe_strcpy(dist_pretty, strchr(buf, ':') + 1); strip_whitespace(dist_pretty); } if (strstr(buf, "Distributor ID:")) { found_something = true; safe_strcpy(dist_name, strchr(buf, ':') + 1); strip_whitespace(dist_name); } if (strstr(buf, "Release:")) { found_something = true; safe_strcpy(dist_version, strchr(buf, ':') + 1); strip_whitespace(dist_version); } if (strstr(buf, "Codename:")) { found_something = true; safe_strcpy(dist_codename, strchr(buf, ':') + 1); strip_whitespace(dist_codename); } } break; } case osrelease: { for (unsigned int i = 0; i < lines.size(); ++i) { safe_strcpy(buf, lines[i].c_str()); strip_whitespace(buf); // check if substr is at the beginning of the line if (strstr(buf, "PRETTY_NAME=") == buf) { found_something = true; safe_strcpy(buf2, strchr(buf, '=') + 1); strip_quotes(buf2); unescape_os_release(buf2); safe_strcpy(dist_pretty, buf2); continue; } if (strstr(buf, "NAME=") == buf) { found_something = true; safe_strcpy(buf2, strchr(buf, '=') + 1); strip_quotes(buf2); unescape_os_release(buf2); safe_strcpy(dist_name, buf2); continue; } if (strstr(buf, "VERSION=") == buf) { found_something = true; safe_strcpy(buf2, strchr(buf, '=') + 1); strip_quotes(buf2); unescape_os_release(buf2); safe_strcpy(dist_version, buf2); continue; } // could also be "UBUNTU_CODENAME=" if (strstr(buf, "CODENAME=")) { found_something = true; safe_strcpy(buf2, strchr(buf, '=') + 1); strip_quotes(buf2); unescape_os_release(buf2); safe_strcpy(dist_codename, buf2); continue; } } break; } case redhatrelease: { safe_strcpy(buf, lines.front().c_str()); found_something = true; strip_whitespace(buf); safe_strcpy(dist_pretty, buf); break; } default: { return false; } } if (found_something) { safe_strcpy(buf2, ""); if (strlen(dist_pretty)) { safe_strcat(buf2, dist_pretty); } else { if (strlen(dist_name)) { safe_strcat(buf2, dist_name); safe_strcat(buf2, " "); } if (strlen(dist_version)) { safe_strcat(buf2, dist_version); safe_strcat(buf2, " "); } if (strlen(dist_codename)) { safe_strcat(buf2, dist_codename); safe_strcat(buf2, " "); } strip_whitespace(buf2); } strlcpy(os_version, buf2, os_version_size); if (strlen(dist_name)) { strlcpy(os_name, dist_name, os_name_size); } return true; } return false; } bool parse_linux_os_info(FILE* file, const LINUX_OS_INFO_PARSER parser, char* os_name, const int os_name_size, char* os_version, const int os_version_size) { if (!file) { return false; } char buf[256]; std::vector lines; while (fgets(buf, 256, file)) { lines.push_back(buf); } return parse_linux_os_info(lines, parser, os_name, os_name_size, os_version, os_version_size); } bool parse_linux_os_info(const std::string& line, const LINUX_OS_INFO_PARSER parser, char* os_name, const int os_name_size, char* os_version, const int os_version_size) { if (line.empty()) { return false; } const char delim = '\n'; return parse_linux_os_info(split(line, delim), parser, os_name, os_name_size, os_version, os_version_size); } int main(void) { char buf[256], features[1024], model_buf[1024]; bool vendor_found=false, model_found=false; bool cache_found=false, features_found=false; bool icache_found=false,dcache_found=false; bool model_hack=false, vendor_hack=false; int n; #if !defined(__aarch64__) && !defined(__arm__) int family=-1, model=-1, stepping=-1; #else char implementer[32] = {0}, architecture[32] = {0}, variant[32] = {0}, cpu_part[32] = {0}, revision[32] = {0}; bool model_info_found=false; #endif char p_vendor[256], p_model[256], product_name[256]; char os_name[256], os_version[256]; string os_version_extra(""); char buf2[256]; int m_cache=-1; // parse /proc/cpuinfo as in parse_cpuinfo_linux() from hostinfo_unix.cpp FILE* f = fopen("/proc/cpuinfo", "r"); if (!f) return (EXIT_FAILURE); safe_strcpy(p_model, ""); #ifdef __mips__ safe_strcpy(p_model, "MIPS "); model_hack = true; #elif __alpha__ safe_strcpy(p_vendor, "HP (DEC) "); vendor_hack = true; #elif __hppa__ safe_strcpy(p_vendor, "HP "); vendor_hack = true; #elif __ia64__ safe_strcpy(p_model, "IA-64 "); model_hack = true; #elif defined(__arm__) || defined(__aarch64__) safe_strcpy(p_vendor, "ARM "); vendor_hack = vendor_found = true; #endif safe_strcpy(features, ""); safe_strcpy(product_name, ""); while (fgets(buf, 256, f)) { strip_whitespace(buf); if ( /* there might be conflicts if we dont #ifdef */ #ifdef __ia64__ strstr(buf, "vendor : ") #elif __hppa__ strstr(buf, "cpu\t\t: ") #elif __powerpc__ strstr(buf, "machine\t\t: ") #elif __sparc__ strstr(buf, "type\t\t: ") #elif __alpha__ strstr(buf, "cpu\t\t\t: ") #else strstr(buf, "vendor_id\t: ") || strstr(buf, "system type\t\t: ") #endif ) { if (!vendor_hack && !vendor_found) { vendor_found = true; strlcpy(p_vendor, strchr(buf, ':') + 2, sizeof(p_vendor)); } else if (!vendor_found) { vendor_found = true; strlcpy(buf2, strchr(buf, ':') + 2, sizeof(p_vendor) - strlen(p_vendor) - 1); safe_strcat(p_vendor, buf2); } } #if defined(__aarch64__) || defined(__arm__) if ( // Hardware is specifying the board this CPU is on, store it in product_name while we parse /proc/cpuinfo strstr(buf, "Hardware\t: ") ) { // this makes sure we only ever copy as much bytes as we can still store in host.product_name int t = sizeof(product_name) - strlen(product_name) - 2; strlcpy(buf2, strchr(buf, ':') + 2, ((t ==buf necessary */ if ((strstr(buf, "model\t\t: ") == buf) && model<0) { model = atoi(buf+strlen("model\t\t: ")); } /* ia64 */ if (strstr(buf, "model : ") && model<0) { model = atoi(buf+strlen("model : ")); } #endif #if !defined(__aarch64__) && !defined(__arm__) if (strstr(buf, "stepping\t: ") && stepping<0) { stepping = atoi(buf+strlen("stepping\t: ")); } #else if (strstr(buf, "CPU implementer") && strlen(implementer) == 0) { strlcpy(implementer, strchr(buf, ':') + 2, sizeof(implementer)); model_info_found = true; } if (strstr(buf, "CPU architecture") && strlen(architecture) == 0) { strlcpy(architecture, strchr(buf, ':') + 2, sizeof(architecture)); model_info_found = true; } if (strstr(buf, "CPU variant") && strlen(variant) == 0) { strlcpy(variant, strchr(buf, ':') + 2, sizeof(variant)); model_info_found = true; } if (strstr(buf, "CPU part") && strlen(cpu_part) == 0) { strlcpy(cpu_part, strchr(buf, ':') + 2, sizeof(cpu_part)); model_info_found = true; } if (strstr(buf, "CPU revision") && strlen(revision) == 0) { strlcpy(revision, strchr(buf, ':') + 2, sizeof(revision)); model_info_found = true; } #endif #ifdef __hppa__ bool icache_found=false,dcache_found=false; if (!icache_found && strstr(buf, "I-cache\t\t: ")) { icache_found = true; sscanf(buf, "I-cache\t\t: %d", &n); m_cache += n*1024; } if (!dcache_found && strstr(buf, "D-cache\t\t: ")) { dcache_found = true; sscanf(buf, "D-cache\t\t: %d", &n); m_cache += n*1024; } #elif __powerpc__ if (!cache_found && strstr(buf, "L2 cache\t: ")) { cache_found = true; sscanf(buf, "L2 cache\t: %d", &n); m_cache = n*1024; } #else if (!cache_found && (strstr(buf, "cache size\t: ") == buf)) { cache_found = true; sscanf(buf, "cache size\t: %d", &n); m_cache = n*1024; } #endif if (!features_found) { // Some versions of the linux kernel call them flags, // others call them features, so look for both. // if ((strstr(buf, "flags\t\t: ") == buf)) { strlcpy(features, strchr(buf, ':') + 2, sizeof(features)); } else if ((strstr(buf, "features\t\t: ") == buf)) { strlcpy(features, strchr(buf, ':') + 2, sizeof(features)); } else if ((strstr(buf, "features : ") == buf)) { /* ia64 */ strlcpy(features, strchr(buf, ':') + 2, sizeof(features)); } else if ((strstr(buf, "Features\t: ") == buf)) { /* arm */ strlcpy(features, strchr(buf, ':') + 2, sizeof(features)); } if (strlen(features)) { features_found = true; } } } strlcpy(model_buf, p_model, sizeof(model_buf)); #if !defined(__aarch64__) && !defined(__arm__) if (family>=0 || model>=0 || stepping>0) { safe_strcat(model_buf, " ["); if (family>=0) { snprintf(buf, sizeof(buf), "Family %d ", family); safe_strcat(model_buf, buf); } if (model>=0) { snprintf(buf, sizeof(buf), "Model %d ", model); safe_strcat(model_buf, buf); } if (stepping>=0) { snprintf(buf, sizeof(buf), "Stepping %d", stepping); safe_strcat(model_buf, buf); } safe_strcat(model_buf, "]"); } #else if (model_info_found) { safe_strcat(model_buf, " ["); if (strlen(implementer)>0) { snprintf(buf, sizeof(buf), "Impl %s ", implementer); safe_strcat(model_buf, buf); } if (strlen(architecture)>0) { snprintf(buf, sizeof(buf), "Arch %s ", architecture); safe_strcat(model_buf, buf); } if (strlen(variant)>0) { snprintf(buf, sizeof(buf), "Variant %s ", variant); safe_strcat(model_buf, buf); } if (strlen(cpu_part)>0) { snprintf(buf, sizeof(buf), "Part %s ", cpu_part); safe_strcat(model_buf, buf); } if (strlen(revision)>0) { snprintf(buf, sizeof(buf), "Rev %s", revision); safe_strcat(model_buf, buf); } safe_strcat(model_buf, "]"); } #endif if (strlen(features)) { safe_strcat(model_buf, "["); safe_strcat(model_buf, features); safe_strcat(model_buf, "]"); } printf("p_vendor: %s\nm_cache: %d\nmodel_buf: %s\n", p_vendor, m_cache, model_buf ); fclose(f); // detect OS name as in HOST_INFO::get_os_info() safe_strcpy(os_name, ""); safe_strcpy(os_version, ""); #if HAVE_SYS_UTSNAME_H struct utsname u; uname(&u); #ifdef ANDROID safe_strcpy(os_name, "Android"); #else safe_strcpy(os_name, u.sysname); #endif //ANDROID #if defined(__EMX__) // OS2: version is in u.version safe_strcpy(os_version, u.version); #elif defined(__HAIKU__) snprintf(os_version, sizeof(os_version), "%s, %s", u.release, u.version); #else safe_strcpy(os_version, u.release); #endif #ifdef _HPUX_SOURCE safe_strcpy(p_model, u.machine); safe_strcpy(p_vendor, "Hewlett-Packard"); #endif #elif HAVE_SYS_SYSCTL_H && defined(CTL_KERN) && defined(KERN_OSTYPE) && defined(KERN_OSRELEASE) mib[0] = CTL_KERN; mib[1] = KERN_OSTYPE; len = sizeof(os_name); sysctl(mib, 2, &os_name, &len, NULL, 0); mib[0] = CTL_KERN; mib[1] = KERN_OSRELEASE; len = sizeof(os_version); sysctl(mib, 2, &os_version, &len, NULL, 0); #elif HAVE_SYS_SYSTEMINFO_H sysinfo(SI_SYSNAME, os_name, sizeof(os_name)); sysinfo(SI_RELEASE, os_version, sizeof(os_version)); #else #error Need to specify a method to obtain OS name/version #endif #if LINUX_LIKE_SYSTEM bool found_something = false; char dist_pretty[256], dist_name[256], dist_version[256], dist_codename[256]; safe_strcpy(dist_pretty, ""); safe_strcpy(dist_name, ""); safe_strcpy(dist_version, ""); safe_strcpy(dist_codename, ""); // see: http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/lsbrelease.html // although the output is not clearly specified it seems to be constant f = popen(command_lsbrelease, "r"); if (f) { found_something = parse_linux_os_info(f, lsbrelease, dist_name, sizeof(dist_name), dist_version, sizeof(dist_version)); pclose(f); } if (!found_something) { // see: https://www.freedesktop.org/software/systemd/man/os-release.html f = fopen(file_osrelease, "r"); if (f) { found_something = parse_linux_os_info(f, osrelease, dist_name, sizeof(dist_name), dist_version, sizeof(dist_version)); fclose(f); } } if (!found_something) { // last ditch effort for older redhat releases f = fopen(file_redhatrelease, "r"); if (f) { found_something = parse_linux_os_info(f, redhatrelease, dist_name, sizeof(dist_name), dist_version, sizeof(dist_version)); fclose(f); } } if (found_something) { os_version_extra = (string)os_version; safe_strcpy(os_version, dist_version); if (strlen(dist_name)) { safe_strcat(os_name, " "); safe_strcat(os_name, dist_name); } } string libc_version(""), libc_extra_info(""); if (!get_libc_version(libc_version, libc_extra_info)) { // add info to os_version_extra if (!os_version_extra.empty()) { os_version_extra += "|"; } os_version_extra += "libc " + libc_version; if (!libc_extra_info.empty()) { os_version_extra += " (" + libc_extra_info + ")"; } } if (!os_version_extra.empty()) { safe_strcat(os_version, " ["); safe_strcat(os_version, os_version_extra.c_str()); safe_strcat(os_version, "]"); } #endif //LINUX_LIKE_SYSTEM printf("os_name: %s\nos_version: %s\nproduct_name: %s\n", os_name, os_version, product_name ); }