From ede61a24ea77e8e4e67e57dab09d054199315bd1 Mon Sep 17 00:00:00 2001 From: Christian Beer Date: Mon, 20 Jun 2016 08:39:24 +0200 Subject: [PATCH] Client: improve Linux OS detection Gathers more information about the Linux OS using lsb_release, /etc/os-release or /etc/redhat-release (in that order) and adds that information to the os_name and os_version fields. This produces information like this: os_name: Linux CentOS Linux os_version: 3.10.0-327.18.2.el7.x86_64 (CentOS Linux 7 (Core)) os_name: Linux os_version: 2.6.32-642.1.1.el6.x86_64 (CentOS release 6.8 (Final)) os_name: Linux Ubuntu os_version: 3.14.65-68 (Ubuntu 16.04 LTS) product_name: ODROID-C2 os_name: Linux Debian os_version: 4.5.0-2-amd64 (Debian GNU/Linux testing (stretch)) This should work on most major distributions and will give a better overview on what Distribution is used by volunteers. --- client/hostinfo_unix.cpp | 118 +++++++++++++++++++++++++++++++++++++++ lib/hostinfo.cpp | 1 + lib/hostinfo.h | 1 - lib/str_util.cpp | 54 ++++++++++++++++++ lib/str_util.h | 2 + 5 files changed, 175 insertions(+), 1 deletion(-) diff --git a/client/hostinfo_unix.cpp b/client/hostinfo_unix.cpp index b4fa221cdd..aa42b61c7f 100644 --- a/client/hostinfo_unix.cpp +++ b/client/hostinfo_unix.cpp @@ -1589,6 +1589,124 @@ int HOST_INFO::get_os_info() { #else #error Need to specify a method to obtain OS name/version #endif + +#if LINUX_LIKE_SYSTEM + bool found_something = false; + char buf[256],buf2[256]; + char dist_pretty[256], dist_name[256], dist_version[256], dist_codename[256]; + strcpy(dist_pretty, ""); + strcpy(dist_name, ""); + strcpy(dist_version, ""); + 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 + FILE* f = popen("/usr/bin/lsb_release -a 2>&1", "r"); + if (f) { + while (fgets(buf, 256, f)) { + 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); + } + } + pclose(f); + } + if (!found_something) { + // see: https://www.freedesktop.org/software/systemd/man/os-release.html + f = fopen("/etc/os-release", "r"); + if (f) { + while (fgets(buf, 256, f)) { + 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); + safe_strcpy(dist_pretty, buf2); + continue; + } + if ( strstr(buf, "NAME=") == buf ) { + found_something = true; + safe_strcpy(buf2, strchr(buf, '=') + 1); + strip_quotes(buf2); + safe_strcpy(dist_name, buf2); + continue; + } + if ( strstr(buf, "VERSION=") == buf ) { + found_something = true; + safe_strcpy(buf2, strchr(buf, '=') + 1); + strip_quotes(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); + safe_strcpy(dist_codename, buf2); + continue; + } + } + fclose(f); + } + } + + if (!found_something) { + // last ditch effort for older redhat releases + f = fopen("/etc/redhat-release", "r"); + if (f) { + fgets(buf, 256, f); + found_something = true; + strip_whitespace(buf); + safe_strcpy(dist_pretty, buf); + fclose(f); + } + } + + if (found_something) { + strcat(os_version, " ("); + if (strlen(dist_pretty)) { + strcat(os_version, dist_pretty); + } else { + if (strlen(dist_name)) { + strcat(os_version, dist_name); + strcat(os_version, " "); + } + if (strlen(dist_version)) { + strcat(os_version, dist_version); + strcat(os_version, " "); + } + if (strlen(dist_codename)) { + strcat(os_version, dist_codename); + strcat(os_version, " "); + } + strip_whitespace(os_version); + } + strcat(os_version, ")"); + if (strlen(dist_name)) { + strcat(os_name, " "); + strcat(os_name, dist_name); + } + } +#endif //LINUX_LIKE_SYSTEM return 0; } diff --git a/lib/hostinfo.cpp b/lib/hostinfo.cpp index 5ecb729ccf..6895f3d97e 100644 --- a/lib/hostinfo.cpp +++ b/lib/hostinfo.cpp @@ -124,6 +124,7 @@ int HOST_INFO::parse(XML_PARSER& xp, bool static_items_only) { if (xp.parse_double("d_free", d_free)) continue; if (xp.parse_str("os_name", os_name, sizeof(os_name))) continue; if (xp.parse_str("os_version", os_version, sizeof(os_version))) continue; + if (xp.parse_str("product_name", product_name, sizeof(product_name))) continue; if (xp.parse_str("virtualbox_version", virtualbox_version, sizeof(virtualbox_version))) continue; if (xp.match_tag("coprocs")) { this->coprocs.parse(xp); diff --git a/lib/hostinfo.h b/lib/hostinfo.h index a04d08e09f..6a030c6dc2 100644 --- a/lib/hostinfo.h +++ b/lib/hostinfo.h @@ -62,7 +62,6 @@ public: char os_name[256]; char os_version[256]; char product_name[256]; // manufacturer and/or model of system - // currently used for Android devices char mac_address[256]; // MAC addr e.g. 00:00:00:00:00:00 // currently populated for Android diff --git a/lib/str_util.cpp b/lib/str_util.cpp index f4e0589c4f..36c4748bbe 100644 --- a/lib/str_util.cpp +++ b/lib/str_util.cpp @@ -332,6 +332,60 @@ void strip_whitespace(string& str) { str.erase(n, str.length()-n); } +// remove whitespace and quotes from start and end of a string +// +void strip_quotes(char *str) { + char *s = str; + + while (*s) { + if (*s == '"' || *s == '\'') { + s++; + continue; + } + if (!isascii(*s)) break; + if (!isspace(*s)) break; + s++; + } + if (s != str) strcpy_overlap(str, s); + + size_t n = strlen(str); + while (n>0) { + n--; + if (str[n] == '"' || str[n] == '\'') { + str[n] = 0; + continue; + } + if (!isascii(str[n])) break; + if (!isspace(str[n])) break; + str[n] = 0; + } +} + +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] == '\'') { + n--; + continue; + } + if (!isascii(str[n-1])) break; + if (!isspace(str[n-1])) break; + n--; + } + str.erase(n, str.length()-n); +} + char* time_to_string(double t) { static char buf[100]; if (!t) { diff --git a/lib/str_util.h b/lib/str_util.h index 4af9e85319..a139690174 100644 --- a/lib/str_util.h +++ b/lib/str_util.h @@ -29,6 +29,8 @@ extern int parse_command_line(char*, char**); extern void c2x(char *what); extern void strip_whitespace(char *str); extern void strip_whitespace(std::string&); +extern void strip_quotes(char *str); +extern void strip_quotes(std::string&); extern char* time_to_string(double); extern char* precision_time_to_string(double); extern void secs_to_hmsf(double, char*);