1 /*
2 Copyright (C) 2015 - 2018 by Iris Morelle <shadowm2006@gmail.com>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16
17 #include "desktop/version.hpp"
18
19 #include "filesystem.hpp"
20 #include "formatter.hpp"
21 #include "gettext.hpp"
22 #include "log.hpp"
23 #include "serialization/unicode.hpp"
24
25 #include <cstring>
26
27 #if defined(__APPLE__)
28
29 #include "apple_version.hpp"
30
31 #elif defined(_X11)
32
33 #include <cerrno>
34 #include <sys/utsname.h>
35
36 #endif
37
38 #ifdef _WIN32
39
40 #ifndef UNICODE
41 #define UNICODE
42 #endif
43 #define WIN32_LEAN_AND_MEAN
44
45 #include <windows.h>
46
47 #endif
48
49 static lg::log_domain log_desktop("desktop");
50 #define ERR_DU LOG_STREAM(err, log_desktop)
51 #define LOG_DU LOG_STREAM(info, log_desktop)
52
53 namespace desktop
54 {
55
56 namespace
57 {
58
59 #ifdef _WIN32
60 /**
61 * Detects whether we are running on Wine or not.
62 *
63 * This is for informational purposes only and all Windows code should assume
64 * we are running on the real thing instead.
65 */
on_wine()66 bool on_wine()
67 {
68 HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
69 if(!ntdll) {
70 return false;
71 }
72
73 return GetProcAddress(ntdll, "wine_get_version") != nullptr;
74 }
75 #endif
76
77 #if defined(_X11)
78 /**
79 * Release policy for POSIX pipe streams opened with popen(3).
80 */
81 struct posix_pipe_release_policy
82 {
operator ()desktop::__anon89d778720111::posix_pipe_release_policy83 void operator()(std::FILE* f) const { if(f != nullptr) { pclose(f); } }
84 };
85
86 /**
87 * Scoped POSIX pipe stream.
88 *
89 * The stream object type is the same as a regular file stream, but the release
90 * policy is different, as required by popen(3).
91 */
92 typedef std::unique_ptr<std::FILE, posix_pipe_release_policy> scoped_posix_pipe;
93
94 /**
95 * Read a single line from the specified pipe.
96 *
97 * @returns An empty string if the pipe is invalid or nothing could be read.
98 */
read_pipe_line(scoped_posix_pipe & p)99 std::string read_pipe_line(scoped_posix_pipe& p)
100 {
101 if(!p.get()) {
102 return "";
103 }
104
105 std::string ver;
106 int c;
107
108 ver.reserve(64);
109
110 // We only want the first line.
111 while((c = std::fgetc(p.get())) && c != EOF && c != '\n' && c != '\r') {
112 ver.push_back(static_cast<char>(c));
113 }
114
115 return ver;
116 }
117 #endif
118
119 } // end anonymous namespace
120
os_version()121 std::string os_version()
122 {
123 #if defined(__APPLE__)
124
125 //
126 // Standard Mac OS X version
127 //
128
129 return desktop::apple::os_version();
130
131 #elif defined(_X11)
132
133 //
134 // Linux Standard Base version.
135 //
136
137 static const std::string lsb_release_bin = "/usr/bin/lsb_release";
138
139 if(filesystem::file_exists(lsb_release_bin)) {
140 static const std::string cmdline = lsb_release_bin + " -s -d";
141
142 scoped_posix_pipe p(popen(cmdline.c_str(), "r"));
143 std::string ver = read_pipe_line(p);
144
145 if(ver.length() >= 2 && ver[0] == '"' && ver[ver.length() - 1] == '"') {
146 ver.erase(ver.length() - 1, 1);
147 ver.erase(0, 1);
148 }
149
150 // Check this again in case we got "" above for some weird reason.
151 if(!ver.empty()) {
152 return ver;
153 }
154 }
155
156 //
157 // POSIX uname version fallback.
158 //
159
160 utsname u;
161
162 if(uname(&u) != 0) {
163 ERR_DU << "os_version: uname error (" << strerror(errno) << ")\n";
164 }
165
166 return formatter() << u.sysname << ' '
167 << u.release << ' '
168 << u.version << ' '
169 << u.machine;
170
171 #elif defined(_WIN32)
172
173 //
174 // Windows version.
175 //
176
177 static const std::string base
178 = !on_wine() ? "Microsoft Windows" : "Wine/Microsoft Windows";
179
180 OSVERSIONINFOEX v { sizeof(OSVERSIONINFOEX) };
181
182 #ifdef _MSC_VER
183 // GetVersionEx is rather problematic, but it works for our usecase.
184 // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
185 // for more info.
186 #pragma warning(push)
187 #pragma warning(disable:4996)
188 #endif
189 if(!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&v))) {
190 ERR_DU << "os_version: GetVersionEx error ("
191 << GetLastError() << ")\n";
192 return base;
193 }
194 #ifdef _MSC_VER
195 #pragma warning(pop)
196 #endif
197
198 const DWORD vnum = v.dwMajorVersion * 100 + v.dwMinorVersion;
199 std::string version;
200
201 switch(vnum)
202 {
203 case 500:
204 version = "2000";
205 break;
206 case 501:
207 version = "XP";
208 break;
209 case 502:
210 // This will misidentify XP x64 but who really cares?
211 version = "Server 2003";
212 break;
213 case 600:
214 if(v.wProductType == VER_NT_WORKSTATION) {
215 version = "Vista";
216 } else {
217 version = "Server 2008";
218 }
219 break;
220 case 601:
221 if(v.wProductType == VER_NT_WORKSTATION) {
222 version = "7";
223 } else {
224 version = "Server 2008 R2";
225 }
226 break;
227 case 602:
228 if(v.wProductType == VER_NT_WORKSTATION) {
229 version = "8";
230 } else {
231 version = "Server 2012";
232 }
233 break;
234 case 603:
235 if(v.wProductType == VER_NT_WORKSTATION) {
236 version = "8.1";
237 } else {
238 version = "Server 2012 R2";
239 }
240 break;
241 case 1000:
242 if(v.wProductType == VER_NT_WORKSTATION) {
243 version = "10";
244 break;
245 } // else fallback to default
246 default:
247 if(v.wProductType != VER_NT_WORKSTATION) {
248 version = "Server";
249 }
250 }
251
252 if(v.szCSDVersion && *v.szCSDVersion) {
253 version += " ";
254 version += unicode_cast<std::string>(std::wstring(v.szCSDVersion));
255 }
256
257 version += " (";
258 // Add internal version numbers.
259 version += formatter()
260 << v.dwMajorVersion << '.'
261 << v.dwMinorVersion << '.'
262 << v.dwBuildNumber;
263 version += ")";
264
265 return base + " " + version;
266
267 #else
268
269 //
270 // "I don't know where I am" version.
271 //
272
273 ERR_DU << "os_version(): unsupported platform\n";
274 return _("operating_system^<unknown>");
275
276 #endif
277 }
278
279 } // end namespace desktop
280