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