Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / utils / SystemInfo.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.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, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <limits.h>
22
23 #include "threads/SystemClock.h"
24 #include "system.h"
25 #include "SystemInfo.h"
26 #ifndef TARGET_POSIX
27 #include <conio.h>
28 #else
29 #include <sys/utsname.h>
30 #endif
31 #include "GUIInfoManager.h"
32 #include "filesystem/CurlFile.h"
33 #include "network/Network.h"
34 #include "Application.h"
35 #include "windowing/WindowingFactory.h"
36 #include "guilib/LocalizeStrings.h"
37 #include "CPUInfo.h"
38 #include "utils/TimeUtils.h"
39 #include "utils/log.h"
40 #ifdef TARGET_WINDOWS
41 #include "dwmapi.h"
42 #endif
43 #if defined(TARGET_DARWIN)
44 #include "osx/DarwinUtils.h"
45 #include "osx/CocoaInterface.h"
46 #endif
47 #include "powermanagement/PowerManager.h"
48 #include "utils/StringUtils.h"
49 #include "utils/XMLUtils.h"
50 #if defined(TARGET_ANDROID)
51 #include "android/jni/Build.h"
52 #include "utils/AMLUtils.h"
53 #endif
54
55 /* Target identification */
56 #if defined(TARGET_DARWIN)
57 #include <Availability.h>
58 #elif defined(TARGET_ANDROID)
59 #include <android/api-level.h>
60 #elif defined(TARGET_FREEBSD)
61 #include <sys/param.h>
62 #elif defined(TARGET_LINUX)
63 #include <linux/version.h>
64 #endif
65
66 CSysInfo g_sysinfo;
67
68 CSysInfoJob::CSysInfoJob()
69 {
70 }
71
72 bool CSysInfoJob::DoWork()
73 {
74   m_info.systemUptime      = GetSystemUpTime(false);
75   m_info.systemTotalUptime = GetSystemUpTime(true);
76   m_info.internetState     = GetInternetState();
77   m_info.videoEncoder      = GetVideoEncoder();
78   m_info.cpuFrequency      = GetCPUFreqInfo();
79   m_info.kernelVersion     = CSysInfo::GetKernelVersion();
80   m_info.macAddress        = GetMACAddress();
81   m_info.batteryLevel      = GetBatteryLevel();
82   return true;
83 }
84
85 const CSysData &CSysInfoJob::GetData() const
86 {
87   return m_info;
88 }
89
90 CStdString CSysInfoJob::GetCPUFreqInfo()
91 {
92   double CPUFreq = GetCPUFrequency();
93   return StringUtils::Format("%4.2fMHz", CPUFreq);;
94 }
95
96 CSysData::INTERNET_STATE CSysInfoJob::GetInternetState()
97 {
98   // Internet connection state!
99   XFILE::CCurlFile http;
100   if (http.IsInternet())
101     return CSysData::CONNECTED;
102   if (http.IsInternet(false))
103     return CSysData::NO_DNS;
104   return CSysData::DISCONNECTED;
105 }
106
107 CStdString CSysInfoJob::GetMACAddress()
108 {
109 #if defined(HAS_LINUX_NETWORK) || defined(HAS_WIN32_NETWORK)
110   CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface();
111   if (iface)
112     return iface->GetMacAddress();
113 #endif
114   return "";
115 }
116
117 CStdString CSysInfoJob::GetVideoEncoder()
118 {
119   return "GPU: " + g_Windowing.GetRenderRenderer();
120 }
121
122 CStdString CSysInfoJob::GetBatteryLevel()
123 {
124   return StringUtils::Format("%d%%", g_powerManager.BatteryLevel());
125 }
126
127 double CSysInfoJob::GetCPUFrequency()
128 {
129 #if defined (TARGET_POSIX) || defined(TARGET_WINDOWS)
130   return double (g_cpuInfo.getCPUFrequency());
131 #else
132   return 0;
133 #endif
134 }
135
136 bool CSysInfoJob::SystemUpTime(int iInputMinutes, int &iMinutes, int &iHours, int &iDays)
137 {
138   iMinutes=0;iHours=0;iDays=0;
139   iMinutes = iInputMinutes;
140   if (iMinutes >= 60) // Hour's
141   {
142     iHours = iMinutes / 60;
143     iMinutes = iMinutes - (iHours *60);
144   }
145   if (iHours >= 24) // Days
146   {
147     iDays = iHours / 24;
148     iHours = iHours - (iDays * 24);
149   }
150   return true;
151 }
152
153 CStdString CSysInfoJob::GetSystemUpTime(bool bTotalUptime)
154 {
155   CStdString strSystemUptime;
156   int iInputMinutes, iMinutes,iHours,iDays;
157
158   if(bTotalUptime)
159   {
160     //Total Uptime
161     iInputMinutes = g_sysinfo.GetTotalUptime() + ((int)(XbmcThreads::SystemClockMillis() / 60000));
162   }
163   else
164   {
165     //Current UpTime
166     iInputMinutes = (int)(XbmcThreads::SystemClockMillis() / 60000);
167   }
168
169   SystemUpTime(iInputMinutes,iMinutes, iHours, iDays);
170   if (iDays > 0)
171   {
172     strSystemUptime = StringUtils::Format("%i %s, %i %s, %i %s",
173                                           iDays, g_localizeStrings.Get(12393).c_str(),
174                                           iHours, g_localizeStrings.Get(12392).c_str(),
175                                           iMinutes, g_localizeStrings.Get(12391).c_str());
176   }
177   else if (iDays == 0 && iHours >= 1 )
178   {
179     strSystemUptime = StringUtils::Format("%i %s, %i %s",
180                                           iHours, g_localizeStrings.Get(12392).c_str(),
181                                           iMinutes, g_localizeStrings.Get(12391).c_str());
182   }
183   else if (iDays == 0 && iHours == 0 &&  iMinutes >= 0)
184   {
185     strSystemUptime = StringUtils::Format("%i %s",
186                                           iMinutes, g_localizeStrings.Get(12391).c_str());
187   }
188   return strSystemUptime;
189 }
190
191 CStdString CSysInfo::TranslateInfo(int info) const
192 {
193   switch(info)
194   {
195   case SYSTEM_VIDEO_ENCODER_INFO:
196     return m_info.videoEncoder;
197   case NETWORK_MAC_ADDRESS:
198     return m_info.macAddress;
199   case SYSTEM_KERNEL_VERSION:
200     return m_info.kernelVersion;
201   case SYSTEM_CPUFREQUENCY:
202     return m_info.cpuFrequency;
203   case SYSTEM_UPTIME:
204     return m_info.systemUptime;
205   case SYSTEM_TOTALUPTIME:
206     return m_info.systemTotalUptime;
207   case SYSTEM_INTERNET_STATE:
208     if (m_info.internetState == CSysData::CONNECTED)
209       return g_localizeStrings.Get(13296);
210     else if (m_info.internetState == CSysData::NO_DNS)
211       return g_localizeStrings.Get(13274);
212     else
213       return g_localizeStrings.Get(13297);
214   case SYSTEM_BATTERY_LEVEL:
215     return m_info.batteryLevel;
216   default:
217     return "";
218   }
219 }
220
221 void CSysInfo::Reset()
222 {
223   m_info.Reset();
224 }
225
226 CSysInfo::CSysInfo(void) : CInfoLoader(15 * 1000)
227 {
228   memset(MD5_Sign, 0, sizeof(MD5_Sign));
229   m_iSystemTimeTotalUp = 0;
230 }
231
232 CSysInfo::~CSysInfo()
233 {
234 }
235
236 bool CSysInfo::Load(const TiXmlNode *settings)
237 {
238   if (settings == NULL)
239     return false;
240   
241   const TiXmlElement *pElement = settings->FirstChildElement("general");
242   if (pElement)
243     XMLUtils::GetInt(pElement, "systemtotaluptime", m_iSystemTimeTotalUp, 0, INT_MAX);
244
245   return true;
246 }
247
248 bool CSysInfo::Save(TiXmlNode *settings) const
249 {
250   if (settings == NULL)
251     return false;
252
253   TiXmlNode *generalNode = settings->FirstChild("general");
254   if (generalNode == NULL)
255   {
256     TiXmlElement generalNodeNew("general");
257     generalNode = settings->InsertEndChild(generalNodeNew);
258     if (generalNode == NULL)
259       return false;
260   }
261   XMLUtils::SetInt(generalNode, "systemtotaluptime", m_iSystemTimeTotalUp);
262
263   return true;
264 }
265
266 bool CSysInfo::GetDiskSpace(const CStdString& drive,int& iTotal, int& iTotalFree, int& iTotalUsed, int& iPercentFree, int& iPercentUsed)
267 {
268   bool bRet= false;
269   ULARGE_INTEGER ULTotal= { { 0 } };
270   ULARGE_INTEGER ULTotalFree= { { 0 } };
271
272   if( !drive.empty() && !drive.Equals("*") )
273   {
274 #ifdef TARGET_WINDOWS
275     UINT uidriveType = GetDriveType(( drive + ":\\" ));
276     if(uidriveType != DRIVE_UNKNOWN && uidriveType != DRIVE_NO_ROOT_DIR)
277 #endif
278       bRet= ( 0 != GetDiskFreeSpaceEx( ( drive + ":\\" ), NULL, &ULTotal, &ULTotalFree) );
279   }
280   else
281   {
282     ULARGE_INTEGER ULTotalTmp= { { 0 } };
283     ULARGE_INTEGER ULTotalFreeTmp= { { 0 } };
284 #ifdef TARGET_WINDOWS
285     char* pcBuffer= NULL;
286     DWORD dwStrLength= GetLogicalDriveStrings( 0, pcBuffer );
287     if( dwStrLength != 0 )
288     {
289       dwStrLength+= 1;
290       pcBuffer= new char [dwStrLength];
291       GetLogicalDriveStrings( dwStrLength, pcBuffer );
292       int iPos= 0;
293       do {
294         if( DRIVE_FIXED == GetDriveType( pcBuffer + iPos  ) &&
295             GetDiskFreeSpaceEx( ( pcBuffer + iPos ), NULL, &ULTotal, &ULTotalFree ) )
296         {
297           ULTotalTmp.QuadPart+= ULTotal.QuadPart;
298           ULTotalFreeTmp.QuadPart+= ULTotalFree.QuadPart;
299         }
300         iPos += (strlen( pcBuffer + iPos) + 1 );
301       }while( strlen( pcBuffer + iPos ) > 0 );
302     }
303     delete[] pcBuffer;
304 #else // for linux and osx
305     static const char *drv_letter[] = { "C:\\", "E:\\", "F:\\", "G:\\", "X:\\", "Y:\\", "Z:\\", NULL };
306     for( int i = 0; drv_letter[i]; i++)
307     {
308       if( GetDiskFreeSpaceEx( drv_letter[i], NULL, &ULTotal, &ULTotalFree ) )
309       {
310         ULTotalTmp.QuadPart+= ULTotal.QuadPart;
311         ULTotalFreeTmp.QuadPart+= ULTotalFree.QuadPart;
312       }
313     }
314 #endif
315     if( ULTotalTmp.QuadPart || ULTotalFreeTmp.QuadPart )
316     {
317       ULTotal.QuadPart= ULTotalTmp.QuadPart;
318       ULTotalFree.QuadPart= ULTotalFreeTmp.QuadPart;
319       bRet= true;
320     }
321   }
322
323   if( bRet )
324   {
325     iTotal = (int)( ULTotal.QuadPart / MB );
326     iTotalFree = (int)( ULTotalFree.QuadPart / MB );
327     iTotalUsed = iTotal - iTotalFree;
328     if( ULTotal.QuadPart > 0 )
329     {
330       iPercentUsed = (int)( 100.0f * ( ULTotal.QuadPart - ULTotalFree.QuadPart ) / ULTotal.QuadPart + 0.5f );
331     }
332     else
333     {
334       iPercentUsed = 0;
335     }
336     iPercentFree = 100 - iPercentUsed;
337   }
338
339   return bRet;
340 }
341
342 CStdString CSysInfo::GetCPUModel()
343 {
344   return "CPU: " + g_cpuInfo.getCPUModel();
345 }
346
347 CStdString CSysInfo::GetCPUBogoMips()
348 {
349   return "BogoMips: " + g_cpuInfo.getCPUBogoMips();
350 }
351
352 CStdString CSysInfo::GetCPUHardware()
353 {
354   return "Hardware: " + g_cpuInfo.getCPUHardware();
355 }
356
357 CStdString CSysInfo::GetCPURevision()
358 {
359   return "Revision: " + g_cpuInfo.getCPURevision();
360 }
361
362 CStdString CSysInfo::GetCPUSerial()
363 {
364   return "Serial: " + g_cpuInfo.getCPUSerial();
365 }
366
367 CStdString CSysInfo::GetManufacturer()
368 {
369   CStdString manufacturer = "";
370 #if defined(TARGET_ANDROID)
371   manufacturer = CJNIBuild::MANUFACTURER;
372 #endif
373   return manufacturer;
374 }
375
376 CStdString CSysInfo::GetModel()
377 {
378   CStdString model = "";
379 #if defined(TARGET_ANDROID)
380   model = CJNIBuild::MODEL;
381 #endif
382   return model;
383 }
384
385 CStdString CSysInfo::GetProduct()
386 {
387   CStdString product = "";
388 #if defined(TARGET_ANDROID)
389   product = CJNIBuild::PRODUCT;
390 #endif
391   return product;
392 }
393
394 bool CSysInfo::IsAeroDisabled()
395 {
396 #ifdef TARGET_WINDOWS
397   BOOL aeroEnabled = FALSE;
398   HRESULT res = DwmIsCompositionEnabled(&aeroEnabled);
399   if (SUCCEEDED(res))
400     return !aeroEnabled;
401 #endif
402   return false;
403 }
404
405 bool CSysInfo::HasHW3DInterlaced()
406 {
407 #if defined(TARGET_ANDROID)
408   if (aml_hw3d_present())
409     return true;
410 #endif
411   return false;
412 }
413
414 CSysInfo::WindowsVersion CSysInfo::m_WinVer = WindowsVersionUnknown;
415
416 bool CSysInfo::IsWindowsVersion(WindowsVersion ver)
417 {
418   if (ver == WindowsVersionUnknown)
419     return false;
420   return GetWindowsVersion() == ver;
421 }
422
423 bool CSysInfo::IsWindowsVersionAtLeast(WindowsVersion ver)
424 {
425   if (ver == WindowsVersionUnknown)
426     return false;
427   return GetWindowsVersion() >= ver;
428 }
429
430 CSysInfo::WindowsVersion CSysInfo::GetWindowsVersion()
431 {
432 #ifdef TARGET_WINDOWS
433   if (m_WinVer == WindowsVersionUnknown)
434   {
435     OSVERSIONINFOEX osvi;
436     ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
437     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
438     if (GetVersionEx((OSVERSIONINFO *)&osvi))
439     {
440       if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
441         m_WinVer = WindowsVersionVista;
442       else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
443         m_WinVer = WindowsVersionWin7;
444       else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
445         m_WinVer = WindowsVersionWin8;
446       else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) 
447         m_WinVer = WindowsVersionWin8_1;
448       /* Insert checks for new Windows versions here */
449       else if ( (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 3) || osvi.dwMajorVersion > 6)
450         m_WinVer = WindowsVersionFuture;
451     }
452   }
453 #endif // TARGET_WINDOWS
454   return m_WinVer;
455 }
456
457 int CSysInfo::GetKernelBitness(void)
458 {
459 #ifdef TARGET_WINDOWS
460   SYSTEM_INFO si;
461   GetSystemInfo(&si);
462   if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
463     return 64;
464   
465   BOOL (WINAPI *ptrIsWow64) (HANDLE, PBOOL);
466   HMODULE hKernel32 = GetModuleHandleA("kernel32");
467   if (hKernel32 == NULL)
468     return 0; // Can't detect OS
469   ptrIsWow64 = (BOOL (WINAPI *) (HANDLE, PBOOL)) GetProcAddress(hKernel32, "IsWow64Process");
470   BOOL wow64proc = FALSE;
471   if (ptrIsWow64 == NULL || ptrIsWow64(GetCurrentProcess(), &wow64proc) == FALSE)
472     return 0; // Can't detect OS
473   return (wow64proc == FALSE) ? 32 : 64;
474 #elif defined(TARGET_POSIX)
475   struct utsname un;
476   if (uname(&un) == 0)
477   {
478     std::string machine(un.machine);
479     if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" || machine == "ppc64" || machine == "ia64")
480       return 64;
481     return 32;
482   }
483   return 0; // can't detect
484 #else
485   return 0; // unknown
486 #endif
487 }
488
489 int CSysInfo::GetXbmcBitness(void)
490 {
491 #if defined (__aarch64__) || defined(__arm64__) || defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || defined(__ppc64__)
492   return 64;
493 #elif defined(__thumb__) || defined(_M_ARMT) || defined(__arm__) || defined(_M_ARM) || defined(__mips__) || defined(mips) || defined(__mips) || defined(i386) || \
494   defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_) || defined(__powerpc) || \
495   defined(__powerpc__) || defined(__ppc__) || defined(_M_PPC)
496   return 32;
497 #else
498   return 0; // Unknown
499 #endif
500 }
501
502 CStdString CSysInfo::GetKernelVersion()
503 {
504 #if defined(TARGET_DARWIN)
505   return g_sysinfo.GetUnameVersion();
506 #elif defined (TARGET_POSIX)
507   struct utsname un;
508   if (uname(&un)==0)
509   {
510     return StringUtils::Format("%s %s %s %s", un.sysname, un.release, un.version, un.machine);;
511   }
512
513   return "";
514 #else
515   OSVERSIONINFOEX osvi;
516   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
517   osvi.dwOSVersionInfoSize = sizeof(osvi);
518
519   std::string strKernel = "Windows";
520   if (GetVersionEx((OSVERSIONINFO *)&osvi))
521   {
522     switch (GetWindowsVersion())
523     {
524     case WindowsVersionVista:
525       if (osvi.wProductType == VER_NT_WORKSTATION)
526         strKernel.append(" Vista");
527       else
528         strKernel.append(" Server 2008");
529       break;
530     case WindowsVersionWin7:
531       if (osvi.wProductType == VER_NT_WORKSTATION)
532         strKernel.append(" 7");
533       else
534         strKernel.append(" Server 2008 R2");
535       break;
536     case WindowsVersionWin8:
537       if (osvi.wProductType == VER_NT_WORKSTATION)
538         strKernel.append(" 8");
539       else
540         strKernel.append(" Server 2012");
541       break;
542     case WindowsVersionWin8_1:
543       if (osvi.wProductType == VER_NT_WORKSTATION)
544         strKernel.append(" 8.1");
545       else
546         strKernel.append(" Server 2012 R2");
547       break;
548     case WindowsVersionFuture:
549       strKernel.append(" Unknown Future Version");
550       break;
551     default:
552       strKernel.append(" Unknown version");
553       break;
554     }
555
556     // Append Service Pack version if any
557     if (osvi.wServicePackMajor > 0)
558     {
559       strKernel.append(StringUtils::Format(" SP%d", osvi.wServicePackMajor));
560       if (osvi.wServicePackMinor > 0)
561       {
562         strKernel.append(StringUtils::Format(".%d", osvi.wServicePackMinor));
563       }
564     }
565
566     strKernel.append(StringUtils::Format(" %d-bit", GetKernelBitness()));
567
568     strKernel.append(StringUtils::Format(", build %d", osvi.dwBuildNumber));
569   }
570   else
571   {
572     strKernel.append(" unknown");
573     strKernel.append(StringUtils::Format(" %d-bit", GetKernelBitness()));
574   }
575
576   return strKernel;
577 #endif
578 }
579
580 bool CSysInfo::HasInternet()
581 {
582   if (m_info.internetState != CSysData::UNKNOWN)
583     return m_info.internetState == CSysData::CONNECTED;
584   return (m_info.internetState = CSysInfoJob::GetInternetState()) == CSysData::CONNECTED;
585 }
586
587 CStdString CSysInfo::GetHddSpaceInfo(int drive, bool shortText)
588 {
589  int percent;
590  return GetHddSpaceInfo( percent, drive, shortText);
591 }
592
593 CStdString CSysInfo::GetHddSpaceInfo(int& percent, int drive, bool shortText)
594 {
595   int total, totalFree, totalUsed, percentFree, percentused;
596   CStdString strRet;
597   percent = 0;
598   if (g_sysinfo.GetDiskSpace("", total, totalFree, totalUsed, percentFree, percentused))
599   {
600     if (shortText)
601     {
602       switch(drive)
603       {
604         case SYSTEM_FREE_SPACE:
605           percent = percentFree;
606           break;
607         case SYSTEM_USED_SPACE:
608           percent = percentused;
609           break;
610       }
611     }
612     else
613     {
614       switch(drive)
615       {
616       case SYSTEM_FREE_SPACE:
617         strRet = StringUtils::Format("%i MB %s", totalFree, g_localizeStrings.Get(160).c_str());
618         break;
619       case SYSTEM_USED_SPACE:
620         strRet = StringUtils::Format("%i MB %s", totalUsed, g_localizeStrings.Get(20162).c_str());
621         break;
622       case SYSTEM_TOTAL_SPACE:
623         strRet = StringUtils::Format("%i MB %s", total, g_localizeStrings.Get(20161).c_str());
624         break;
625       case SYSTEM_FREE_SPACE_PERCENT:
626         strRet = StringUtils::Format("%i %% %s", percentFree, g_localizeStrings.Get(160).c_str());
627         break;
628       case SYSTEM_USED_SPACE_PERCENT:
629         strRet = StringUtils::Format("%i %% %s", percentused, g_localizeStrings.Get(20162).c_str());
630         break;
631       }
632     }
633   }
634   else
635   {
636     if (shortText)
637       strRet = "N/A";
638     else
639       strRet = g_localizeStrings.Get(161);
640   }
641   return strRet;
642 }
643
644 #if defined(TARGET_LINUX)
645 CStdString CSysInfo::GetLinuxDistro()
646 {
647 #if defined(TARGET_ANDROID)
648   return "Android";
649 #endif
650   static const char* release_file[] = { "/etc/debian_version",
651                                         "/etc/SuSE-release",
652                                         "/etc/mandrake-release",
653                                         "/etc/fedora-release",
654                                         "/etc/redhat-release",
655                                         "/etc/gentoo-release",
656                                         "/etc/slackware-version",
657                                         "/etc/arch-release",
658                                         "/etc/buildroot-release",
659                                         NULL };
660   CStdString result("");
661   char buffer[256] = {'\0'};
662
663   /* Try reading PRETTY_NAME from /etc/os-release first.
664    * If this fails, fall back to lsb_release or distro-specific release-file. */
665
666   FILE *os_release = fopen("/etc/os-release", "r");
667
668   if (os_release)
669   {
670     char *key = NULL;
671     char *val = NULL;
672
673     while (fgets(buffer, sizeof(buffer), os_release))
674     {
675       key = val = buffer;
676       strsep(&val, "=");
677
678       if (strcmp(key, "PRETTY_NAME") == 0)
679       {
680         char *pretty_name = val;
681
682         // remove newline and enclosing quotes
683         if (pretty_name[strlen(pretty_name) - 1] == '\n')
684           pretty_name[strlen(pretty_name) - 1] = '\0';
685
686         if (pretty_name[0] == '\'' || pretty_name[0] == '\"')
687         {
688           pretty_name++;
689           pretty_name[strlen(pretty_name) - 1] = '\0';
690         }
691
692         // unescape quotes and backslashes
693         char *p = pretty_name;
694         while (*p)
695         {
696           char *this_char = p;
697           char *next_char = p + 1;
698
699           if (*this_char == '\\' &&
700               (*next_char == '\'' || *next_char == '\"' || *next_char == '\\'))
701           {
702             while (*this_char)
703             {
704               *this_char = *next_char;
705               this_char++;
706               next_char++;
707             }
708           }
709
710           p++;
711         }
712
713         result = pretty_name;
714         break;
715       }
716     }
717
718     fclose(os_release);
719
720     if (!result.empty())
721       return result;
722   }
723
724   FILE* pipe = popen("unset PYTHONHOME; unset PYTHONPATH; lsb_release -d  2>/dev/null | cut -f2", "r");
725   if (pipe)
726   {
727     if (fread(buffer, sizeof(char), sizeof(buffer), pipe) > 0 && !ferror(pipe))
728       result = buffer;
729     pclose(pipe);
730     if (!result.empty())
731       return StringUtils::Trim(result);
732   }
733
734   FILE* file = NULL;
735   for (int i = 0; result.empty() && release_file[i]; i++)
736   {
737     file = fopen(release_file[i], "r");
738     if (file)
739     {
740       if (fgets(buffer, sizeof(buffer), file))
741       {
742         result = buffer;
743         if (!result.empty())
744           return StringUtils::Trim(result);
745       }
746       fclose(file);
747     }
748   }
749
750   CLog::Log(LOGWARNING, "Unable to determine Linux distribution");
751   return "Unknown";
752 }
753 #endif
754
755 #ifdef TARGET_POSIX
756 CStdString CSysInfo::GetUnameVersion()
757 {
758   CStdString result = "";
759
760 #if defined(TARGET_ANDROID)
761   struct utsname name;
762   if (uname(&name) == -1)
763     result = "Android";
764   result += name.release;
765   result += " ";
766   result += name.machine;
767 #elif defined(TARGET_DARWIN_IOS)
768   result = GetDarwinOSReleaseString();
769   result += ", ";
770   result += GetDarwinVersionString();
771 #else
772   FILE* pipe = popen("uname -rm", "r");
773   if (pipe)
774   {
775     char buffer[256];
776     if (fgets(buffer, sizeof(buffer), pipe))
777     {
778       result = buffer;
779 #if defined(TARGET_DARWIN)
780       StringUtils::Trim(result);
781       result += ", "; 
782       result += GetDarwinVersionString();
783 #endif
784     }
785     else
786       CLog::Log(LOGWARNING, "Unable to determine Uname version");
787     pclose(pipe);
788   }
789 #endif//else !TARGET_ANDROID
790
791   return StringUtils::Trim(result);
792 }
793 #endif
794
795 #if defined(TARGET_WINDOWS)
796 CStdString CSysInfo::GetUAWindowsVersion()
797 {
798   OSVERSIONINFOEX osvi = {};
799
800   osvi.dwOSVersionInfoSize = sizeof(osvi);
801   CStdString strVersion = "Windows NT";
802
803   if (GetVersionEx((OSVERSIONINFO *)&osvi))
804   {
805     strVersion += StringUtils::Format(" %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
806   }
807
808   SYSTEM_INFO si = {};
809   GetSystemInfo(&si);
810
811   BOOL bIsWow = FALSE;
812   if (IsWow64Process(GetCurrentProcess(), &bIsWow))
813   {
814     if (bIsWow)
815     {
816       strVersion.append(";WOW64");
817       GetNativeSystemInfo(&si);     // different function to read the info under Wow
818     }
819   }
820
821   if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
822     strVersion.append(";Win64;x64");
823   else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64)
824     strVersion.append(";Win64;IA64");
825
826   return strVersion;
827 }
828 #endif
829
830
831 CStdString CSysInfo::GetUserAgent()
832 {
833   CStdString result;
834   result = "XBMC/" + g_infoManager.GetLabel(SYSTEM_BUILD_VERSION) + " (";
835 #if defined(TARGET_WINDOWS)
836   result += GetUAWindowsVersion();
837 #elif defined(TARGET_DARWIN)
838 #if defined(TARGET_DARWIN_IOS)
839   result += "iOS; ";
840 #else
841   result += "Mac OS X; ";
842 #endif
843   result += GetUnameVersion();
844 #elif defined(TARGET_FREEBSD)
845   result += "FreeBSD; ";
846   result += GetUnameVersion();
847 #elif defined(TARGET_POSIX)
848   result += "Linux; ";
849   result += GetLinuxDistro();
850   result += "; ";
851   result += GetUnameVersion();
852 #endif
853   result += "; http://xbmc.org)";
854
855   return result;
856 }
857
858 bool CSysInfo::IsAppleTV2()
859 {
860 #if defined(TARGET_DARWIN)
861   return DarwinIsAppleTV2();
862 #else
863   return false;
864 #endif
865 }
866
867 bool CSysInfo::HasVideoToolBoxDecoder()
868 {
869   bool        result = false;
870
871 #if defined(HAVE_VIDEOTOOLBOXDECODER)
872   result = DarwinHasVideoToolboxDecoder();
873 #endif
874   return result;
875 }
876
877 std::string CSysInfo::GetBuildTargetPlatformName(void)
878 {
879 #if defined(TARGET_DARWIN_OSX)
880   return "Darwin OSX";
881 #elif defined(TARGET_DARWIN_IOS_ATV2)
882   return "Darwin iOS ATV2";
883 #elif defined(TARGET_DARWIN_IOS)
884   return "Darwin iOS";
885 #elif defined(TARGET_FREEBSD)
886   return "FreeBSD";
887 #elif defined(TARGET_ANDROID)
888   return "Android";
889 #elif defined(TARGET_LINUX)
890   return "Linux";
891 #elif defined(TARGET_WINDOWS)
892   return "Win32";
893 #else
894   return "unknown platform";
895 #endif
896 }
897
898 std::string CSysInfo::GetBuildTargetPlatformVersion(void)
899 {
900 /* Expand macro before stringify */
901 #define STR_MACRO(x) #x
902 #define XSTR_MACRO(x) STR_MACRO(x)
903
904 #if defined(TARGET_DARWIN_OSX)
905   return "version " XSTR_MACRO(__MAC_OS_X_VERSION_MIN_REQUIRED);
906 #elif defined(TARGET_DARWIN_IOS)
907   return "version " XSTR_MACRO(__IPHONE_OS_VERSION_MIN_REQUIRED);
908 #elif defined(TARGET_FREEBSD)
909   return "version " XSTR_MACRO(__FreeBSD_version);
910 #elif defined(TARGET_ANDROID)
911   return "API level " XSTR_MACRO(__ANDROID_API__);
912 #elif defined(TARGET_LINUX)
913   std::string ver = StringUtils::Format("%i.%i.%i", LINUX_VERSION_CODE >> 16, (LINUX_VERSION_CODE >> 8) & 0xff, LINUX_VERSION_CODE & 0xff);
914   return ver;
915 #elif defined(TARGET_WINDOWS)
916   return "version " XSTR_MACRO(NTDDI_VERSION);
917 #else
918   return "(unknown platform)";
919 #endif
920 }
921
922 std::string CSysInfo::GetBuildTargetCpuFamily(void)
923 {
924 #if defined(__thumb__) || defined(_M_ARMT) 
925   return "ARM (Thumb)";
926 #elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
927   return "ARM";
928 #elif defined(__mips__) || defined(mips) || defined(__mips)
929   return "MIPS";
930 #elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
931    defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_)
932   return "x86";
933 #elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC)
934   return "PowerPC";
935 #else
936   return "unknown CPU family";
937 #endif
938 }
939
940
941 CJob *CSysInfo::GetJob() const
942 {
943   return new CSysInfoJob();
944 }
945
946 void CSysInfo::OnJobComplete(unsigned int jobID, bool success, CJob *job)
947 {
948   m_info = ((CSysInfoJob *)job)->GetData();
949   CInfoLoader::OnJobComplete(jobID, success, job);
950 }