1
2<?php
3
4/*
5	Phoronix Test Suite
6	URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/
7	Copyright (C) 2008 - 2021, Phoronix Media
8	Copyright (C) 2008 - 2021, Michael Larabel
9	phodevi_system.php: The PTS Device Interface object for the system software
10
11	This program is free software; you can redistribute it and/or modify
12	it under the terms of the GNU General Public License as published by
13	the Free Software Foundation; either version 3 of the License, or
14	(at your option) any later version.
15
16	This program is distributed in the hope that it will be useful,
17	but WITHOUT ANY WARRANTY; without even the implied warranty of
18	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19	GNU General Public License for more details.
20
21	You should have received a copy of the GNU General Public License
22	along with this program. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25class phodevi_system extends phodevi_device_interface
26{
27	public static $report_wine_override = false;
28
29	public static function properties()
30	{
31		return array(
32			'username' => new phodevi_device_property('sw_username', phodevi::std_caching),
33			'hostname' => new phodevi_device_property('sw_hostname', phodevi::smart_caching),
34			'vendor-identifier' => new phodevi_device_property('sw_vendor_identifier', phodevi::smart_caching),
35			'filesystem' => new phodevi_device_property('sw_filesystem', phodevi::no_caching),
36			'virtualized-mode' => new phodevi_device_property('sw_virtualized_mode', phodevi::smart_caching),
37			'java-version' => new phodevi_device_property('sw_java_version', phodevi::std_caching),
38			'python-version' => new phodevi_device_property('sw_python_version', phodevi::std_caching),
39			'wine-version' => new phodevi_device_property('sw_wine_version', phodevi::std_caching),
40			'display-server' => new phodevi_device_property('sw_display_server', phodevi::smart_caching),
41			'display-driver' => new phodevi_device_property(array('sw_display_driver', false), phodevi::smart_caching),
42			'display-driver-string' => new phodevi_device_property(array('sw_display_driver', true), phodevi::smart_caching),
43			'dri-display-driver' => new phodevi_device_property('sw_dri_display_driver', phodevi::smart_caching),
44			'opengl-driver' => new phodevi_device_property('sw_opengl_driver', phodevi::std_caching),
45			'vulkan-driver' => new phodevi_device_property('sw_vulkan_driver', phodevi::std_caching),
46			'opencl-driver' => new phodevi_device_property('sw_opencl_driver', phodevi::std_caching),
47			'opengl-vendor' => new phodevi_device_property('sw_opengl_vendor', phodevi::smart_caching),
48			'desktop-environment' => new phodevi_device_property('sw_desktop_environment', phodevi::smart_caching),
49			'operating-system' => new phodevi_device_property('sw_operating_system', phodevi::smart_caching),
50			'os-version' => new phodevi_device_property('sw_os_version', phodevi::smart_caching),
51			'kernel' => new phodevi_device_property('sw_kernel', phodevi::smart_caching),
52			'kernel-architecture' => new phodevi_device_property('sw_kernel_architecture', phodevi::smart_caching),
53			'kernel-date' => new phodevi_device_property('sw_kernel_date', phodevi::smart_caching),
54			'kernel-string' => new phodevi_device_property('sw_kernel_string', phodevi::smart_caching),
55			'kernel-parameters' => new phodevi_device_property('sw_kernel_parameters', phodevi::std_caching),
56			'compiler' => new phodevi_device_property('sw_compiler', phodevi::no_caching),
57			'system-layer' => new phodevi_device_property('sw_system_layer', phodevi::no_caching),
58			'environment-variables' => new phodevi_device_property('sw_environment_variables', phodevi::std_caching),
59			'security-features' => new phodevi_device_property('sw_security_features', phodevi::std_caching),
60			'kernel-extra-details' => new phodevi_device_property('sw_kernel_extra_details', phodevi::std_caching),
61			'battery' => new phodevi_device_property('battery', phodevi::smart_caching),
62			'platform-profile' => new phodevi_device_property('sw_platform_profile', phodevi::std_caching),
63			);
64	}
65	public static function sw_username()
66	{
67		// Gets the system user's name
68		if(function_exists('posix_getpwuid') && function_exists('posix_getuid'))
69		{
70			$userinfo = posix_getpwuid(posix_getuid());
71			$username = $userinfo['name'];
72		}
73		else
74		{
75			$username = trim(getenv('USERNAME'));
76		}
77
78		return $username;
79	}
80	public static function sw_platform_profile()
81	{
82		$platform_profile = '';
83
84		if(phodevi::is_linux())
85		{
86			if(is_file('/sys/firmware/acpi/platform_profile'))
87			{
88				$platform_profile = pts_file_io::file_get_contents('/sys/firmware/acpi/platform_profile');
89			}
90		}
91
92		return $platform_profile;
93	}
94	public static function sw_kernel_extra_details()
95	{
96		$extra = array();
97
98		if(phodevi::is_linux())
99		{
100			if(is_file('/sys/kernel/mm/transparent_hugepage/enabled'))
101			{
102				$thp_enabled = file_get_contents('/sys/kernel/mm/transparent_hugepage/enabled');
103				if(($x = strpos($thp_enabled, '[')) !== false)
104				{
105					$thp_enabled = substr($thp_enabled, $x + 1);
106					if(($x = strpos($thp_enabled, ']')) !== false)
107					{
108						$thp_enabled = trim(substr($thp_enabled, 0, $x));
109						if(!empty($thp_enabled))
110						{
111							$extra[] = 'Transparent Huge Pages: ' . $thp_enabled;
112						}
113					}
114				}
115
116			}
117		}
118
119		return implode(' - ', $extra);
120	}
121	public static function sw_system_layer()
122	{
123		$layer = null;
124
125		if(phodevi::is_windows() && pts_client::executable_in_path('winecfg.exe') && ($wine = phodevi::read_property('system', 'wine-version')))
126		{
127			$layer = $wine;
128		}
129		else if((getenv('USE_WINE') || getenv('WINE_VERSION') || self::$report_wine_override) && ($wine = phodevi::read_property('system', 'wine-version')))
130		{
131			$layer = $wine;
132		}
133		else
134		{
135			// Report virtualization
136			$layer = phodevi::read_property('system', 'virtualized-mode');
137		}
138
139		if(empty($layer) && is_file('/proc/version'))
140		{
141			if(stripos(file_get_contents('/proc/version'), 'Microsoft') !== false && stripos(file_get_contents('/proc/mounts'), 'lxfs') !== false)
142			{
143				// Microsoft Windows Subsystem for Linux
144				$layer = 'WSL';
145			}
146		}
147
148		return $layer;
149	}
150	public static function sw_hostname()
151	{
152		$hostname = 'Unknown';
153
154		if(($bin = pts_client::executable_in_path('hostname')))
155		{
156			$hostname = trim(shell_exec($bin . ' 2>&1'));
157		}
158		else if(phodevi::is_windows())
159		{
160			$hostname = getenv('USERDOMAIN');
161		}
162
163		return $hostname;
164	}
165	public static function sw_vendor_identifier()
166	{
167		// Returns the vendor identifier used with the External Dependencies and other distro-specific features
168		$vendor = phodevi::is_linux() ? phodevi_linux_parser::read_lsb_distributor_id() : false;
169
170		if(!$vendor)
171		{
172			$vendor = phodevi::read_property('system', 'operating-system');
173
174			if(($spos = strpos($vendor, ' ')) > 1)
175			{
176				$vendor = substr($vendor, 0, $spos);
177			}
178		}
179
180		return str_replace(array(' ', '/'), '', strtolower($vendor));
181	}
182	public static function sw_filesystem()
183	{
184		// Determine file-system type
185		$fs = null;
186
187		if(phodevi::is_macos())
188		{
189			$fs = phodevi_osx_parser::read_osx_system_profiler('SPSerialATADataType', 'FileSystem', false, array('MS-DOS FAT32'));
190
191			if($fs == null && pts_client::executable_in_path('mount'))
192			{
193				$mount = shell_exec('mount 2>&1');
194				if(stripos($mount, ' on / (hfs, local, journaled)') !== false)
195				{
196					$fs = 'Journaled HFS+';
197				}
198				else if(stripos($mount, ' on / (hfs') !== false)
199				{
200					$fs = 'HFS+';
201				}
202				else if(stripos($mount, ' on / (apfs') !== false)
203				{
204					$fs = 'APFS';
205				}
206			}
207		}
208		else if(phodevi::is_bsd())
209		{
210			if(pts_client::executable_in_path('mount'))
211			{
212				$mount = shell_exec('mount 2>&1');
213
214				if(($start = strpos($mount, 'on / (')) != false)
215				{
216					// FreeBSD, DragonflyBSD mount formatting
217					/*
218					-bash-4.0$ mount
219					ROOT on / (hammer, local)
220					/dev/da0s1a on /boot (ufs, local)
221					/pfs/@@-1:00001 on /var (null, local)
222					/pfs/@@-1:00002 on /tmp (null, local)
223					/pfs/@@-1:00003 on /usr (null, local)
224					/pfs/@@-1:00004 on /home (null, local)
225					/pfs/@@-1:00005 on /usr/obj (null, local)
226					/pfs/@@-1:00006 on /var/crash (null, local)
227					/pfs/@@-1:00007 on /var/tmp (null, local)
228					procfs on /proc (procfs, local)
229					*/
230
231					// TODO: improve this in case there are other partitions, etc
232					$fs = substr($mount, $start + 6);
233					$fs = substr($fs, 0, strpos($fs, ','));
234				}
235				else if(($start = strpos($mount, 'on / type')) != false)
236				{
237					// OpenBSD 5.0 formatting is slightly different from above FreeBSD example
238					// TODO: improve this in case there are other partitions, etc
239					$fs = substr($mount, $start + 10);
240					$fs = substr($fs, 0, strpos($fs, ' '));
241				}
242			}
243		}
244		else if(phodevi::is_hurd())
245		{
246			// Very rudimentary Hurd filesystem detection support but works for at least a clean Debian GNU/Hurd EXT2 install
247			if(pts_client::executable_in_path('mount'))
248			{
249				$mount = shell_exec('mount 2>&1');
250
251				if(($start = strpos($mount, 'on / type')) != false)
252				{
253					$fs = substr($mount, $start + 10);
254					$fs = substr($fs, 0, strpos($fs, ' '));
255
256					if(substr($fs, -2) == 'fs')
257					{
258						$fs = substr($fs, 0, -2);
259					}
260				}
261			}
262		}
263		else if(phodevi::is_linux() || phodevi::is_solaris())
264		{
265			$fs = trim(shell_exec('stat ' . pts_client::test_install_root_path() . ' -L -f -c %T 2> /dev/null'));
266
267			switch($fs)
268			{
269				case 'ext2/ext3':
270					if(isset(phodevi::$vfs->mounts))
271					{
272						$fstab = phodevi::$vfs->mounts;
273						$fstab = str_replace('/boot ', 'IGNORE', $fstab);
274
275						$using_ext2 = strpos($fstab, ' ext2') !== false;
276						$using_ext3 = strpos($fstab, ' ext3') !== false;
277						$using_ext4 = strpos($fstab, ' ext4') !== false;
278
279						if(!$using_ext2 && !$using_ext3 && $using_ext4)
280						{
281							$fs = 'ext4';
282						}
283						else if(!$using_ext2 && !$using_ext4 && $using_ext3)
284						{
285							$fs = 'ext3';
286						}
287						else if(!$using_ext3 && !$using_ext4 && $using_ext2)
288						{
289							$fs = 'ext2';
290						}
291						else if(is_dir('/proc/fs/ext4/'))
292						{
293							$fs = 'ext4';
294						}
295						else if(is_dir('/proc/fs/ext3/'))
296						{
297							$fs = 'ext3';
298						}
299					}
300					break;
301				case 'Case-sensitive Journaled HFS+':
302					$fs = 'HFS+';
303					break;
304				case 'MS-DOS FAT32':
305					$fs = 'FAT32';
306					break;
307				case 'UFSD_NTFS_COMPR':
308					$fs = 'NTFS';
309					break;
310				case 'ecryptfs':
311					if(isset(phodevi::$vfs->mounts))
312					{
313						// An easy attempt to determine what file-system is underneath ecryptfs if being compared
314						// For now just attempt to figure out the root file-system.
315						if(($s = strrpos(phodevi::$vfs->mounts, ' / ')) !== false)
316						{
317							$s = substr(phodevi::$vfs->mounts, ($s + 3));
318							$s = substr($s, 0, strpos($s, ' '));
319
320
321							if($s != null && !isset($s[18]) && $s != 'rootfs'&& pts_strings::string_only_contains($s, pts_strings::CHAR_LETTER | pts_strings::CHAR_NUMERIC))
322							{
323								$fs = $s . ' (ecryptfs)';
324							}
325						}
326					}
327					break;
328				default:
329					if(substr($fs, 0, 9) == 'UNKNOWN (')
330					{
331						$magic_block = substr($fs, 9, -1);
332						$known_magic_blocks = array(
333							'0x9123683e' => 'Btrfs',
334							'0x2fc12fc1' => 'zfs', // KQ Infotech ZFS
335							'0x482b' => 'HFS+',
336							'0x65735546' => 'FUSE',
337							'0x565a4653' => 'ReiserFS',
338							'0x52345362' => 'Reiser4',
339							'0x3434' => 'NILFS2',
340							'0x5346414f' => 'OpenAFS',
341							'0x47504653' => 'GPFS',
342							'0x5941ff53' => 'YAFFS',
343							'0xff534d42' => 'CIFS',
344							'0x24051905' => 'UBIFS',
345							'0x1021994' => 'TMPFS',
346							'0x73717368' => 'SquashFS',
347							'0xc97e8168' => 'LogFS',
348							'0x5346544E' => 'NTFS',
349							'0xf15f' => 'eCryptfs',
350							'0x61756673' => 'AuFS',
351							'0xbd00bd0' => 'Lustre',
352							'0xaad7aaea' => 'PanFS', // Panasas FS
353							'0xf2f52010' => 'F2FS',
354							'0xc36400' => 'CephFS',
355							'0x53464846' => 'WSLFS',
356							'0xca451a4e' => 'BcacheFS'
357							);
358
359						foreach($known_magic_blocks as $hex => $name)
360						{
361							if($magic_block == $hex)
362							{
363								$fs = $name;
364								break;
365							}
366						}
367					}
368					break;
369			}
370
371			if(strpos($fs, 'UNKNOWN') !== false && isset(phodevi::$vfs->mounts))
372			{
373				$mounts = phodevi::$vfs->mounts;
374				$fs_r = array();
375
376				$fs_checks = array(
377					'squashfs' => 'SquashFS',
378					'aufs' => 'AuFS',
379					'unionfs' => 'UnionFS',
380					'overlay' => 'overlayfs',
381					);
382
383				foreach($fs_checks as $fs_module => $fs_name)
384				{
385					if(strpos($mounts, $fs_module) != false)
386					{
387						array_push($fs_r, $fs_name);
388					}
389				}
390
391				if(count($fs_r) > 0)
392				{
393					$fs = implode(' + ', $fs_r);
394				}
395			}
396		}
397		else if(phodevi::is_windows())
398		{
399			// TODO could use better detection to verify if C: or the desired disk under test... but most of the time will be NTFS anyways
400			$fs = filter_var(trim(shell_exec('powershell "(Get-WMIObject -Class Win32_Volume | Select DriveLetter,FreeSpace,Capacity,DeviceID,Label,@{Name=\"FileSystemType\";Expression={$_.\"FileSystem\"}})[1].FileSystemType"')), FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);
401			if(empty($fs) || $fs == 'Unknown' || $fs == 'FAT32')
402			{
403				$fs = filter_var(trim(shell_exec('powershell "(Get-WMIObject -Class Win32_Volume | Select DriveLetter,FreeSpace,Capacity,DeviceID,Label,@{Name=\"FileSystemType\";Expression={$_.\"FileSystem\"}})[0].FileSystemType"')),FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);
404			}
405
406			// Fallback for Windows 8
407			if(empty($fs) || $fs == 'Unknown' || $fs == 'FAT32' || stripos($fs, 'not'))
408			{
409				if(strpos(shell_exec('fsutil fsinfo volumeinfo C:'), 'NTFS') !== false)
410				{
411					$fs = 'NTFS';
412				}
413			}
414		}
415
416		if(empty($fs))
417		{
418			$fs = 'Unknown';
419		}
420
421		return $fs;
422	}
423	public static function sw_virtualized_mode()
424	{
425		// Reports if system is running virtualized
426		$virtualized = null;
427		$mobo = phodevi::read_name('motherboard');
428		$gpu = phodevi::read_name('gpu');
429		$cpu = phodevi::read_property('cpu', 'model');
430
431		if(strpos($cpu, 'QEMU') !== false || (is_readable('/sys/class/dmi/id/bios_vendor') && pts_file_io::file_get_contents('/sys/class/dmi/id/bios_vendor') == 'QEMU'))
432		{
433			$virtualized = 'QEMU';
434
435			if(strpos($cpu, 'QEMU Virtual') !== false)
436			{
437				$qemu_version = substr($cpu, (strrpos($cpu, ' ') + 1));
438
439				if(pts_strings::is_version($qemu_version))
440				{
441					$virtualized .= ' ' . $qemu_version;
442				}
443			}
444		}
445		else if(stripos($gpu, 'VMware') !== false || (is_readable('/sys/class/dmi/id/product_name') && stripos(pts_file_io::file_get_contents('/sys/class/dmi/id/product_name'), 'VMware') !== false))
446		{
447			$virtualized = 'VMware';
448		}
449		else if(stripos($gpu, 'VirtualBox') !== false || stripos(phodevi::read_name('motherboard'), 'VirtualBox') !== false)
450		{
451			$virtualized = 'VirtualBox';
452
453			if($vbox_manage = pts_client::executable_in_path('VBoxManage'))
454			{
455				$vbox_manage = trim(shell_exec($vbox_manage . ' --version 2> /dev/null'));
456
457				if(is_numeric(substr($vbox_manage, 0, 1)))
458				{
459					$virtualized .= ' ' . $vbox_manage;
460				}
461			}
462			else if($modinfo = pts_client::executable_in_path('modinfo'))
463			{
464				$modinfo = trim(shell_exec('modinfo -F version vboxguest 2> /dev/null'));
465
466				if($modinfo != null && pts_strings::is_version(str_ireplace(array('_', 'RC', 'beta'), '', $modinfo)))
467				{
468					$virtualized .= ' ' . $modinfo;
469				}
470			}
471
472		}
473		else if(is_file('/sys/class/dmi/id/sys_vendor') && pts_file_io::file_get_contents('/sys/class/dmi/id/sys_vendor') == 'Xen')
474		{
475			$virtualized = pts_file_io::file_get_contents('/sys/class/dmi/id/product_name');
476
477			if(strpos($virtualized, 'Xen') === false)
478			{
479				$virtualized = 'Xen ' . $virtualized;
480			}
481
482			// version string
483			$virtualized .= ' ' . pts_file_io::file_get_contents('/sys/class/dmi/id/product_version');
484
485			// $virtualized should be then e.g. 'Xen HVM domU 4.1.1'
486		}
487		else if(stripos($gpu, 'Microsoft Hyper-V') !== false)
488		{
489			$virtualized = 'Microsoft Hyper-V Server';
490		}
491		else if(stripos($mobo, 'Parallels Software') !== false)
492		{
493			$virtualized = 'Parallels Virtualization';
494		}
495		else if(is_file('/sys/hypervisor/type'))
496		{
497			$type = pts_file_io::file_get_contents('/sys/hypervisor/type');
498			$version = array();
499
500			foreach(array('major', 'minor', 'extra') as $v)
501			{
502				if(is_file('/sys/hypervisor/version/' . $v))
503				{
504					$v = pts_file_io::file_get_contents('/sys/hypervisor/version/' . $v);
505				}
506				else
507				{
508					continue;
509				}
510
511				if($v != null)
512				{
513					if(!empty($version) && substr($v, 0, 1) != '.')
514					{
515						$v = '.' . $v;
516					}
517					array_push($version, $v);
518				}
519			}
520
521			$virtualized = ucwords($type) . ' ' . implode('', $version) . ' Hypervisor';
522		}
523
524		if($systemd_virt = pts_client::executable_in_path('systemd-detect-virt'))
525		{
526			$systemd_virt = trim(shell_exec($systemd_virt . ' 2> /dev/null'));
527
528			if($systemd_virt != null && $systemd_virt != 'none')
529			{
530				switch($systemd_virt)
531				{
532					case 'kvm':
533						$systemd_virt = 'KVM';
534						break;
535					case 'oracle':
536						$systemd_virt = 'Oracle';
537						break;
538				}
539
540				if($virtualized != null && stripos($virtualized, $systemd_virt) === false && stripos($systemd_virt, $virtualized) === false)
541				{
542					$virtualized = $systemd_virt . ' ' . $virtualized;
543				}
544				else if($virtualized == null)
545				{
546					$virtualized = $systemd_virt;
547				}
548			}
549		}
550
551		if(empty($virtualized))
552		{
553			if(is_file('/.dockerenv'))
554			{
555				$virtualized = 'Docker';
556			}
557			else if(is_file('/dev/lxc/console'))
558			{
559				$virtualized = 'lxc';
560			}
561		}
562
563		return $virtualized;
564	}
565	public static function sw_environment_variables()
566	{
567		$check_variables = array('LIBGL', '__GL', 'DRI_', 'DEBUG', 'FLAGS', 'PERF_', 'PERFTEST');
568		$to_report = array();
569
570		if(stripos(phodevi::read_property('system', 'opengl-driver'), 'Mesa'))
571		{
572			array_push($check_variables, 'MESA', 'GALLIUM');
573		}
574
575		if(isset($_SERVER))
576		{
577			foreach($_SERVER as $name => &$value)
578			{
579				foreach($check_variables as $var)
580				{
581					if(stripos($name, $var) !== false && $name != '__GL_SYNC_TO_VBLANK' && strpos($name, 'GJS') === false)
582					{
583						$value = trim($value);
584						if(strpos($value, ' ') !== false)
585						{
586							$value = '"' . $value . '"';
587						}
588						array_push($to_report, $name . '=' . $value);
589						break;
590					}
591				}
592
593			}
594		}
595
596		return implode(' ', array_unique($to_report));
597	}
598	public static function sw_security_features()
599	{
600		$security = array();
601		if(pts_client::executable_in_path('getenforce'))
602		{
603			$selinux = shell_exec('getenforce 2>&1');
604			if(strpos($selinux, 'Enforcing') !== false)
605			{
606				$security[] = 'SELinux';
607			}
608		}
609
610		// Meltdown / KPTI check
611		if(phodevi::is_linux())
612		{
613		/*	if(strpos(phodevi::$vfs->dmesg, 'page tables isolation: enabled') !== false)
614			{
615				// Kernel Page Table Isolation
616				$security[] = 'KPTI';
617			}
618*/
619			// Spectre
620			foreach(pts_file_io::glob('/sys/devices/system/cpu/vulnerabilities/*') as $vuln)
621			{
622				$fc = file_get_contents($vuln);
623				$fc = str_replace('Mitigation: ', 'Mitigation of ', $fc);
624				$fc = str_replace('Speculative Store Bypass', 'SSB', $fc);
625				if(!empty($fc))
626				{
627					$security[] = basename($vuln) . ': ' . $fc;
628				}
629			}
630		}
631		else if(phodevi::is_bsd())
632		{
633			// FreeBSD
634			if(phodevi_bsd_parser::read_sysctl('vm.pmap.pti') == '1')
635			{
636				$security[] = 'KPTI';
637			}
638			if(phodevi_bsd_parser::read_sysctl('hw.ibrs_active') == '1')
639			{
640				$security[] = 'IBRS';
641			}
642
643			// DragonFlyBSD
644			if(($spectre = phodevi_bsd_parser::read_sysctl('machdep.spectre_mitigation')) != '0' && $spectre != 'NONE' && !empty($spectre))
645			{
646				$security[] = 'Spectre ' . $spectre . ' Mitigation';
647			}
648			if(phodevi_bsd_parser::read_sysctl('machdep.meltdown_mitigation') == '1')
649			{
650				$security[] = 'Meltdown Mitigation';
651			}
652		}
653		else if(phodevi::is_windows())
654		{
655			$mds_tool = getenv('USERPROFILE') . '\Downloads\mdstool-cli.exe';
656			if(is_file($mds_tool))
657			{
658				$mds_output = preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', shell_exec($mds_tool));
659				//echo PHP_EOL;
660				foreach(array('__user pointer sanitization: Disabled', 'Retpoline: Full', 'IBPB: Always', 'IBRS: Enabled', 'STIBP: Enabled', 'KPTI Enabled: Yes', 'PTE Inversion: Yes') as $check)
661				{
662					if(stripos($mds_output, $check) !== false)
663					{
664						$security[] = $check;
665					}
666				}
667			}
668		}
669
670		return !empty($security) ? implode(' + ',  $security) : null;
671	}
672	public static function sw_compiler()
673	{
674		// Returns version of the compiler (if present)
675		$compilers = array();
676
677		if($gcc = pts_client::executable_in_path('gcc'))
678		{
679			if(!is_link($gcc) || strpos(readlink($gcc), 'gcc') !== false)
680			{
681				// GCC
682				// If it's a link, ensure that it's not linking to llvm/clang or something
683				$version = trim(shell_exec('gcc -dumpversion 2>&1'));
684				$v = shell_exec('gcc -v 2>&1');
685				if(pts_strings::is_version($version))
686				{
687
688					if(($t = strrpos($v, $version . ' ')) !== false)
689					{
690						$v = substr($v, ($t + strlen($version) + 1));
691						$v = substr($v, 0, strpos($v, ' '));
692
693						if($v != null && is_numeric($v))
694						{
695							// On development versions the release date is expressed
696							// e.g. gcc version 4.7.0 20120314 (prerelease) (GCC)
697							$version .= ' ' . $v;
698						}
699						else
700						{
701							$v = shell_exec('gcc --version 2>&1');
702							if(($t = strrpos($v, $version)) !== false)
703							{
704								$v = substr($v, $t);
705								$v = substr($v, 0, strpos(str_replace(PHP_EOL, ' ', $v), ' '));
706								if(($t = strpos($v, ')')) !== false)
707								{
708									$v = substr($v, 0, $t);
709								}
710
711								if(pts_strings::is_version($v))
712								{
713									$version = $v;
714								}
715							}
716						}
717					}
718
719					$compilers['gcc'] = 'GCC ' . $version;
720				}
721				else if(($t = strpos($v, ' version ')) !== false)
722				{
723					$v = substr($v, ($t + strlen(' version ')));
724					if(($t = strpos($v, ' (')) !== false)
725					{
726						$v = substr($v, 0, $t);
727						$compilers['gcc'] = 'GCC ' . $v;
728					}
729				}
730			}
731			// sometimes "copyright" slips into version string
732			if(isset($compilers['gcc']))
733			{
734				$compilers['gcc'] = str_replace('Copyright', '', $compilers['gcc']);
735			}
736		}
737
738		if(pts_client::executable_in_path('pgcc'))
739		{
740			// NVIDIA PGI Compiler
741			$compilers['pgcc'] = 'PGI Compiler';
742			$v = trim(shell_exec('pgcc --version 2>&1'));
743			$v = substr($v, strpos($v, 'pgcc ') + 5);
744			$v = substr($v, 0, strpos($v, ' '));
745			if(pts_strings::is_version(str_replace('-', '', $v)))
746			{
747				$compilers['pgcc'] .= ' ' . $v;
748			}
749		}
750
751		if(pts_client::executable_in_path('pcc'))
752		{
753			// PCC - Portable C Compiler
754			$pcc = explode(' ', trim(shell_exec('pcc -version 2>&1')));
755
756			if($pcc[0] == 'pcc')
757			{
758				$compilers['pcc'] = 'PCC ' . $pcc[1] . (is_numeric($pcc[2]) ? ' ' . $pcc[2] : null);
759			}
760		}
761
762		if(pts_client::executable_in_path('pgcpp') || pts_client::executable_in_path('pgCC'))
763		{
764			// The Portland Group Compilers
765			$compilers['pgcpp'] = 'PGI C-C++ Workstation';
766		}
767
768		if(($clang = pts_client::executable_in_path('clang')))
769		{
770			// Clang
771			$compiler_info = shell_exec(escapeshellarg($clang) . ' --version');
772			if(($cv_pos = stripos($compiler_info, 'clang version')) !== false)
773			{
774				// With Clang 3.0 and prior, the --version produces output where the first line is:
775				// e.g. clang version 3.0 (branches/release_30 142590)
776
777				$compiler_info = substr($compiler_info, ($cv_pos + 14));
778				$compiler_info = str_replace(PHP_EOL, ' ', $compiler_info);
779				$clang_version = substr($compiler_info, 0, strpos($compiler_info, ' '));
780
781				// XXX: the below check bypass now because e.g. Ubuntu appends '-ubuntuX', etc that breaks check
782				if(pts_strings::is_version($clang_version) || true)
783				{
784					// Also see if there is a Clang SVN tag to fetch
785					$compiler_info = substr($compiler_info, 0, strpos($compiler_info, PHP_EOL));
786					if(($cv_pos = strpos($compiler_info, ')')) !== false)
787					{
788						$compiler_info = substr($compiler_info, 0, $cv_pos);
789						$compiler_info = substr($compiler_info, (strrpos($compiler_info, ' ') + 1));
790
791						if(is_numeric($compiler_info))
792						{
793							// Right now Clang/LLVM uses SVN system and their revisions are only numeric
794							$clang_version .= ' (SVN ' . $compiler_info . ')';
795						}
796					}
797
798					$compiler_info = 'Clang ' . $clang_version;
799				}
800				else
801				{
802					$compiler_info = null;
803				}
804			}
805			else
806			{
807				$compiler_info = substr($compiler_info, 0, strpos($compiler_info, PHP_EOL));
808			}
809
810			// Clang
811			if(empty($compiler_info) && stripos($compiler_info, 'not found'))
812			{
813				// At least with Clang ~3.0 the -dumpversion is reporting '4.2.1' ratherthan the useful information...
814				// This is likely just for GCC command compatibility, so only use this as a fallback
815				$compiler_info = 'Clang ' . trim(shell_exec('clang -dumpversion 2> /dev/null'));
816			}
817
818			$compilers['clang'] = $compiler_info;
819		}
820
821		// For now at least Intel oneAPI has symlink from clang to icpx so this below code isn't needed in such case
822		if(!pts_client::executable_in_path('clang') && ($icpx = pts_client::executable_in_path('icpx')))
823		{
824			// Intel oneAPI DPC++/C++ Compiler
825			$icpx = shell_exec(escapeshellarg($icpx) . ' --version');
826			$icpx = substr($icpx, 0, strpos($icpx, PHP_EOL));
827			if(stripos($icpx, 'oneAPI') !== false)
828			{
829				$icpx = str_ireplace(array('(R)'), '', $icpx);
830				if(($x = strpos($icpx, ' (')) !== false)
831				{
832					$icpx = substr($icpx, 0, $x);
833				}
834			}
835
836			$compilers['icpx'] = $icpx;
837		}
838
839		if(($llvm_ld = pts_client::executable_in_path('llvm-link')) || ($llvm_ld = pts_client::executable_in_path('llvm-ld')))
840		{
841			// LLVM - Low Level Virtual Machine
842			// Reading the version from llvm-ld (the LLVM linker) should be safe as well for finding out version of LLVM in use
843			// As of LLVM 3.2svn, llvm-ld seems to be llvm-link
844
845			$info = trim(shell_exec($llvm_ld . ' -version 2> /dev/null'));
846
847			if(($s = strpos($info, 'version')) != false)
848			{
849				$info = substr($info, 0, strpos($info, PHP_EOL, $s));
850				$info = substr($info, (strrpos($info, ' ') + 1));
851
852				if(pts_strings::is_version(str_replace('svn', '', $info)))
853				{
854					$compilers['llvmc'] = 'LLVM ' . $info;
855				}
856			}
857		}
858		else if(pts_client::executable_in_path('llvm-config'))
859		{
860			// LLVM - Low Level Virtual Machine config
861			$info = trim(shell_exec('llvm-config --version 2> /dev/null'));
862			if(pts_strings::is_version(str_replace('svn', '', $info)))
863			{
864				$compilers['llvmc'] = 'LLVM ' . $info;
865			}
866		}
867		else if(pts_client::executable_in_path('llvmc'))
868		{
869			// LLVM - Low Level Virtual Machine (llvmc)
870			$info = trim(shell_exec('llvmc -version 2>&1'));
871
872			if(($s = strpos($info, 'version')) != false)
873			{
874				$info = substr($info, 0, strpos($info, "\n", $s));
875				$info = substr($info, strrpos($info, "\n"));
876
877				$compilers['llvmc'] = trim($info);
878			}
879		}
880
881		if(pts_client::executable_in_path('suncc'))
882		{
883			// Sun Studio / SunCC
884			$info = trim(shell_exec('suncc -V 2>&1'));
885
886			if(($s = strpos($info, 'Sun C')) != false)
887			{
888				$info = substr($info, $s);
889				$info = substr($info, 0, strpos($info, "\n"));
890
891				$compilers['suncc'] = $info;
892			}
893		}
894
895		if(pts_client::executable_in_path('ioc'))
896		{
897			// Intel Offline Compiler (IOC) SDK for OpenCL
898			// -v e.g. : Intel(R) SDK for OpenCL* - Offline Compiler 2012 Command-Line Client, version 1.0.2
899			$info = trim(shell_exec('ioc -version 2>&1')) . ' ';
900
901			if(($s = strpos($info, 'Offline Compiler ')) != false)
902			{
903				$compilers['ioc'] = 'Intel IOC SDK';
904				$sv = substr($info, ($s + 17));
905				$sv = substr($sv, 0, strpos($sv, ' '));
906
907				if(is_numeric($sv))
908				{
909					$compilers['ioc'] .= ' ' . $sv;
910				}
911
912				if(($s = strpos($info, 'version ')) != false)
913				{
914					$sv = substr($info, ($s + 8));
915					$sv = substr($sv, 0, strpos($sv, ' '));
916
917					if(pts_strings::is_version($sv))
918					{
919						$compilers['ioc'] .= ' v' . $sv;
920					}
921				}
922			}
923		}
924
925		if(($icc = pts_client::executable_in_path('icc')) || ($icc = pts_client::executable_in_path('icpc')))
926		{
927			// Intel oneAPI DPC++/C++ Compiler
928			$icc = shell_exec(escapeshellarg($icc) . ' --version');
929			$icc = substr($icc, 0, strpos($icc, PHP_EOL));
930			if(stripos($icc, 'icc') !== false)
931			{
932				$icc = str_ireplace(array('(R)', '(ICC)', '(C)'), '', $icc);
933				if(($x = strpos($icpx, ' (')) !== false)
934				{
935					$icpx = substr($icc, 0, $x);
936				}
937			}
938
939			$icc = str_replace('icc', 'ICC', $icc);
940			$compilers['icc'] = $icc;
941		}
942
943		if(phodevi::is_macos() && pts_client::executable_in_path('xcodebuild'))
944		{
945			$xcode = phodevi_osx_parser::read_osx_system_profiler('SPDeveloperToolsDataType', 'Xcode');
946			$xcode = substr($xcode, 0, strpos($xcode, ' '));
947
948			if($xcode)
949			{
950				$compilers['Xcode'] = 'Xcode ' . $xcode;
951			}
952		}
953
954		if(($nvcc = pts_client::executable_in_path('nvcc')) || is_executable(($nvcc = '/usr/local/cuda/bin/nvcc')))
955		{
956			// Check outside of PATH too since by default the CUDA Toolkit goes to '/usr/local/cuda/' and relies upon user to update system
957			// NVIDIA CUDA Compiler Driver
958			$nvcc = shell_exec($nvcc . ' --version 2>&1');
959			if(($s = strpos($nvcc, 'release ')) !== false)
960			{
961				$nvcc = str_replace(array(','), '', substr($nvcc, ($s + 8)));
962				$nvcc = substr($nvcc, 0, strpos($nvcc, ' '));
963
964				if(pts_strings::is_version($nvcc))
965				{
966					$compilers['CUDA'] = 'CUDA ' . $nvcc;
967				}
968			}
969		}
970
971		// Try to make the compiler that's used by default to appear first
972		if(pts_client::read_env('CC') && isset($compilers[basename(pts_strings::first_in_string(pts_client::read_env('CC'), ' '))]))
973		{
974			$cc_env = basename(pts_strings::first_in_string(pts_client::read_env('CC'), ' '));
975			$default_compiler = $compilers[$cc_env];
976			unset($compilers[$cc_env]);
977			array_unshift($compilers, $default_compiler);
978		}
979		else if(pts_client::executable_in_path('cc') && is_link(pts_client::executable_in_path('cc')))
980		{
981			$cc_link = basename(readlink(pts_client::executable_in_path('cc')));
982
983			if(isset($compilers[$cc_link]))
984			{
985				$default_compiler = $compilers[$cc_link];
986				unset($compilers[pts_client::read_env('CC')]);
987				array_unshift($compilers, $default_compiler);
988			}
989		}
990
991		return implode(' + ', array_unique($compilers));
992	}
993	public static function sw_kernel_string()
994	{
995		return trim(phodevi::read_property('system', 'kernel') . ' (' . phodevi::read_property('system', 'kernel-architecture') . ') ' . phodevi::read_property('system', 'kernel-date'));
996	}
997	public static function sw_kernel_date()
998	{
999		$date = null;
1000		$k = phodevi::read_property('system', 'kernel');
1001
1002		if(strpos($k, '99') !== false || stripos($k, 'rc') !== false)
1003		{
1004			// For now at least only report kernel build date when it looks like it's a devel kernel
1005			$v = php_uname('v');
1006			if(($x = stripos($v, 'SMP ')) !== false)
1007			{
1008				$v = substr($v, ($x + 4));
1009				$date = strtotime($v);
1010				if($date != false)
1011				{
1012					$date = date('Ymd', $date);
1013				}
1014			}
1015		}
1016
1017		return $date;
1018	}
1019	public static function sw_kernel()
1020	{
1021		return php_uname('r');
1022	}
1023	public static function sw_kernel_parameters()
1024	{
1025		$parameters = null;
1026
1027		if(is_file('/proc/cmdline') && is_file('/proc/modules'))
1028		{
1029			$modules = array();
1030
1031			foreach(explode(PHP_EOL, pts_file_io::file_get_contents('/proc/modules')) as $module_line)
1032			{
1033				$module_line = explode(' ', $module_line);
1034
1035				if(isset($module_line[0]) && !empty($module_line[0]))
1036				{
1037					array_push($modules, $module_line[0]);
1038				}
1039			}
1040
1041			if(!empty($modules))
1042			{
1043				$to_report = array();
1044				$cmdline = explode(' ', pts_file_io::file_get_contents('/proc/cmdline'));
1045				foreach($cmdline as $option)
1046				{
1047					if(($t = strpos($option, '.')) !== false)
1048					{
1049						if(in_array(substr($option, 0, $t), $modules))
1050						{
1051							array_push($to_report, $option);
1052						}
1053					}
1054				}
1055
1056				if(!empty($to_report))
1057				{
1058					$parameters = implode(' ', $to_report);
1059				}
1060			}
1061		}
1062
1063		return $parameters;
1064	}
1065	public static function sw_kernel_architecture()
1066	{
1067		// Find out the kernel archiecture
1068		if(phodevi::is_windows())
1069		{
1070			//$kernel_arch = strpos($_SERVER['PROCESSOR_ARCHITECTURE'], 64) !== false || strpos($_SERVER['PROCESSOR_ARCHITEW6432'], 64 != false) ? 'x86_64' : 'i686';
1071			if(isset($_SERVER['PROCESSOR_ARCHITEW6432']))
1072			{
1073				$kernel_arch = $_SERVER['PROCESSOR_ARCHITEW6432'] == 'AMD64' ? 'x86_64' : 'i686';
1074			}
1075			else
1076			{
1077				$kernel_arch = 'x86_64';
1078			}
1079		}
1080		else
1081		{
1082			$kernel_arch = php_uname('m');
1083
1084			switch($kernel_arch)
1085			{
1086				case 'X86-64':
1087				case 'amd64':
1088					$kernel_arch = 'x86_64';
1089					break;
1090				case 'i86pc':
1091				case 'i586':
1092				case 'i686-AT386':
1093					$kernel_arch = 'i686';
1094					break;
1095			}
1096		}
1097
1098		return $kernel_arch;
1099	}
1100	public static function sw_os_version()
1101	{
1102		// Returns OS version
1103		if(phodevi::is_macos())
1104		{
1105			$os = phodevi_osx_parser::read_osx_system_profiler('SPSoftwareDataType', 'SystemVersion');
1106
1107			$start_pos = strpos($os, '.');
1108			$end_pos = strrpos($os, '.');
1109			$start_pos = strrpos(substr($os, 0, $start_pos), ' ');
1110			$end_pos = strpos($os, ' ', $end_pos);
1111
1112			$os_version = substr($os, $start_pos + 1, $end_pos - $start_pos);
1113		}
1114		else if(phodevi::is_linux())
1115		{
1116			$os_version = phodevi_linux_parser::read_lsb('Release');
1117
1118			if($os_version == null)
1119			{
1120				if(is_readable('/etc/os-release'))
1121					$os_release = parse_ini_file('/etc/os-release');
1122				else if(is_readable('/usr/lib/os-release'))
1123					$os_release = parse_ini_file('/usr/lib/os-release');
1124				else
1125					$os_release = null;
1126
1127				if(isset($os_release['VERSION_ID']) && !empty($os_release['VERSION_ID']))
1128				{
1129					$os_version = $os_release['VERSION_ID'];
1130				}
1131				else if(isset($os_release['VERSION']) && !empty($os_release['VERSION']))
1132				{
1133					$os_version = $os_release['VERSION'];
1134				}
1135				$os_version = pts_strings::keep_in_string($os_version, pts_strings::CHAR_LETTER | pts_strings::CHAR_NUMERIC | pts_strings::CHAR_DECIMAL | pts_strings::CHAR_SPACE | pts_strings::CHAR_DASH | pts_strings::CHAR_UNDERSCORE);
1136			}
1137		}
1138		else if(phodevi::is_windows())
1139		{
1140			$os_version = phodevi_windows_parser::get_wmi_object('win32_operatingsystem', 'BuildNumber');
1141		}
1142		else
1143		{
1144			$os_version = php_uname('r');
1145		}
1146
1147		return $os_version;
1148	}
1149	public static function sw_operating_system()
1150	{
1151		if(!PTS_IS_CLIENT)
1152		{
1153			// TODO: Figure out why this function is sometimes called from OpenBenchmarking.org....
1154			return false;
1155		}
1156
1157		// Determine the operating system release
1158		if(phodevi::is_linux())
1159		{
1160			$vendor = phodevi_linux_parser::read_lsb_distributor_id();
1161
1162			if($vendor == null)
1163			{
1164				if(is_readable('/etc/os-release'))
1165					$os_release = parse_ini_file('/etc/os-release');
1166				else if(is_readable('/usr/lib/os-release'))
1167					$os_release = parse_ini_file('/usr/lib/os-release');
1168				else
1169					$os_release = null;
1170
1171				if(isset($os_release['PRETTY_NAME']) && !empty($os_release['PRETTY_NAME']))
1172				{
1173					$vendor = $os_release['PRETTY_NAME'];
1174				}
1175				else if(isset($os_release['NAME']) && !empty($os_release['NAME']))
1176				{
1177					$vendor = $os_release['NAME'];
1178				}
1179
1180				$vendor = str_replace('é', 'e', $vendor);
1181			}
1182
1183			if(($x = stripos($vendor, ' for ')) !== false)
1184			{
1185				$vendor = substr($vendor, 0, $x);
1186			}
1187
1188			$vendor = str_replace(array(' Software'), '', $vendor);
1189		}
1190		else if(phodevi::is_hurd())
1191		{
1192			$vendor = php_uname('v');
1193		}
1194		else
1195		{
1196			$vendor = null;
1197		}
1198
1199		$version = phodevi::read_property('system', 'os-version');
1200
1201		if(!$vendor)
1202		{
1203			$os = null;
1204
1205			// Try to detect distro for those not supplying lsb_release
1206			$files = pts_file_io::glob('/etc/*-version');
1207			for($i = 0; $i < count($files) && $os == null; $i++)
1208			{
1209				$file = file_get_contents($files[$i]);
1210
1211				if(trim($file) != null)
1212				{
1213					$os = substr($file, 0, strpos($file, "\n"));
1214				}
1215			}
1216
1217			if($os == null)
1218			{
1219				$files = pts_file_io::glob('/etc/*-release');
1220				for($i = 0; $i < count($files) && $os == null; $i++)
1221				{
1222					$file = file_get_contents($files[$i]);
1223
1224					if(trim($file) != null)
1225					{
1226						$proposed_os = substr($file, 0, strpos($file, PHP_EOL));
1227
1228						if(strpos($proposed_os, '=') == false)
1229						{
1230							$os = $proposed_os;
1231						}
1232					}
1233					else if($i == (count($files) - 1))
1234					{
1235						$os = ucwords(substr(($n = basename($files[$i])), 0, strpos($n, '-')));
1236					}
1237				}
1238			}
1239
1240			if($os == null && is_file('/etc/release'))
1241			{
1242				$file = file_get_contents('/etc/release');
1243				$os = substr($file, 0, strpos($file, "\n"));
1244			}
1245
1246			if($os == null && is_file('/etc/palm-build-info'))
1247			{
1248				// Palm / webOS Support
1249				$os = phodevi_parser::parse_equal_delimited_file('/etc/palm-build-info', 'PRODUCT_VERSION_STRING');
1250			}
1251
1252			if($os == null)
1253			{
1254				if(is_file('/etc/debian_version'))
1255				{
1256					$os = 'Debian ' . php_uname('s') . ' ' . ucwords(pts_file_io::file_get_contents('/etc/debian_version'));
1257				}
1258				else
1259				{
1260					$os = php_uname('s');
1261				}
1262			}
1263			else if(strpos($os, ' ') === false)
1264			{
1265				// The OS string is only one word, likely a problem...
1266				if(is_file('/etc/arch-release') && stripos($os, 'Arch') === false)
1267				{
1268					// On at least some Arch installs (ARM) the file is empty so would have missed above check
1269					$os = trim('Arch Linux ' . $os);
1270				}
1271			}
1272		}
1273		else if($version != null && stripos($vendor, $version) === false)
1274		{
1275			$os = $vendor . ' ' . $version;
1276		}
1277		else
1278		{
1279			$os = $vendor;
1280		}
1281
1282		if(($break_point = strpos($os, ':')) > 0)
1283		{
1284			$os = substr($os, $break_point + 1);
1285		}
1286
1287		if(phodevi::is_macos())
1288		{
1289			$os = phodevi_osx_parser::read_osx_system_profiler('SPSoftwareDataType', 'SystemVersion');
1290		}
1291		else if(phodevi::is_windows())
1292		{
1293			$os = $info = phodevi_windows_parser::get_wmi_object('win32_operatingsystem', 'caption') . ' Build ' . phodevi::read_property('system', 'os-version');
1294			if(strpos($os, 'Windows') === false)
1295			{
1296				$os = trim(exec('ver'));
1297			}
1298		}
1299		if(($break_point = strpos($os, '(')) > 0)
1300		{
1301			$os = substr($os, 0, $break_point);
1302		}
1303
1304		$os = trim($os);
1305
1306		return $os;
1307	}
1308	public static function sw_desktop_environment()
1309	{
1310		$desktop = null;
1311		$desktop_environment = null;
1312		$desktop_version = null;
1313		$desktop_session = pts_client::read_env('DESKTOP_SESSION');
1314
1315		if(pts_client::is_process_running('gnome-shell'))
1316		{
1317			// GNOME 3.0 / GNOME Shell
1318			$desktop_environment = 'GNOME Shell';
1319
1320			if(pts_client::executable_in_path('gnome-shell'))
1321			{
1322				$desktop_version = pts_strings::last_in_string(trim(shell_exec('gnome-shell --version 2> /dev/null')));
1323			}
1324		}
1325		else if(pts_client::is_process_running('gnome-panel') || $desktop_session == 'gnome')
1326		{
1327			// GNOME
1328			$desktop_environment = 'GNOME';
1329
1330			if(pts_client::executable_in_path('gnome-about'))
1331			{
1332				$desktop_version = pts_strings::last_in_string(trim(shell_exec('gnome-about --version 2> /dev/null')));
1333			}
1334			else if(pts_client::executable_in_path('gnome-session'))
1335			{
1336				$desktop_version = pts_strings::last_in_string(trim(shell_exec('gnome-session --version 2> /dev/null')));
1337			}
1338		}
1339		else if(pts_client::is_process_running('unity-2d-panel') || $desktop_session == 'ubuntu-2d')
1340		{
1341			// Canonical / Ubuntu Unity 2D Desktop
1342			$desktop_environment = 'Unity 2D';
1343
1344			if(pts_client::executable_in_path('unity'))
1345			{
1346				$desktop_version = pts_strings::last_in_string(trim(shell_exec('unity --version 2> /dev/null')));
1347			}
1348		}
1349		else if(pts_client::is_process_running('unity-panel-service') || $desktop_session == 'ubuntu')
1350		{
1351			// Canonical / Ubuntu Unity Desktop
1352			$desktop_environment = 'Unity';
1353
1354			if(pts_client::executable_in_path('unity'))
1355			{
1356				$desktop_version = pts_strings::last_in_string(trim(shell_exec('unity --version 2> /dev/null')));
1357			}
1358		}
1359		else if($desktop_session == 'mate')
1360		{
1361			$desktop_environment = 'MATE';
1362			if(pts_client::executable_in_path('mate-about'))
1363			{
1364				$desktop_version = pts_strings::last_in_string(trim(shell_exec('mate-about --version 2> /dev/null')));
1365			}
1366		}
1367		else if(($kde5 = pts_client::is_process_running('plasmashell')))
1368		{
1369			// KDE 5.x
1370			$desktop_environment = 'KDE Plasma';
1371			$desktop_version = pts_strings::last_in_string(trim(shell_exec('plasmashell --version 2> /dev/null')));
1372		}
1373		else if(($kde5 = pts_client::is_process_running('kded5')))
1374		{
1375			// KDE 5.x
1376			$desktop_environment = 'KDE Frameworks';
1377			if(pts_client::executable_in_path('kdeinit5'))
1378			{
1379				$desktop_version = pts_strings::last_in_string(trim(shell_exec('kdeinit5 --version 2> /dev/null')));
1380			}
1381		}
1382		else if(($dde = pts_client::is_process_running('dde-desktop')))
1383		{
1384			// KDE 5.x
1385			$desktop_environment = 'Deepin Desktop Environment';
1386			$desktop_version = null; // TODO XXX
1387		}
1388		else if(($kde4 = pts_client::is_process_running('kded4')) || pts_client::is_process_running('kded'))
1389		{
1390			// KDE 4.x
1391			$desktop_environment = 'KDE';
1392			$kde_output = trim(shell_exec(($kde4 ? 'kde4-config' : 'kde-config') . ' --version 2>&1'));
1393			$kde_lines = explode("\n", $kde_output);
1394
1395			for($i = 0; $i < count($kde_lines) && empty($desktop_version); $i++)
1396			{
1397				$line_segments = pts_strings::colon_explode($kde_lines[$i]);
1398
1399				if(in_array($line_segments[0], array('KDE', 'KDE Development Platform')) && isset($line_segments[1]))
1400				{
1401					$v = trim($line_segments[1]);
1402
1403					if(($cut = strpos($v, ' ')) > 0)
1404					{
1405						$v = substr($v, 0, $cut);
1406					}
1407
1408					$desktop_version = $v;
1409				}
1410			}
1411		}
1412		else if(pts_client::is_process_running('chromeos-wm'))
1413		{
1414			$chrome_output = trim(shell_exec('chromeos-wm -version'));
1415
1416			if($chrome_output == 'chromeos-wm')
1417			{
1418				// No version actually reported
1419				$chrome_output = 'Chrome OS';
1420			}
1421
1422			$desktop_environment = $chrome_output;
1423		}
1424		else if(pts_client::is_process_running('lxqt-panel') || $desktop_session == 'lxqt')
1425		{
1426			//$lx_output = trim(shell_exec('lxqt-panel --version'));
1427			//$version = substr($lx_output, strpos($lx_output, ' ') + 1);
1428
1429			$desktop_environment = 'LXQt';
1430			if(pts_client::executable_in_path('lxqt-about'))
1431			{
1432				$desktop_version = pts_strings::last_in_string(trim(shell_exec('lxqt-about --version | grep liblxqt 2> /dev/null')));
1433			}
1434		}
1435		else if(pts_client::is_process_running('lxsession') || $desktop_session == 'lxde')
1436		{
1437			$lx_output = trim(shell_exec('lxpanel --version 2>&1'));
1438			$version = substr($lx_output, strpos($lx_output, ' ') + 1);
1439
1440			$desktop_environment = 'LXDE';
1441			$desktop_version = $version;
1442		}
1443		else if(pts_client::is_process_running('lumina-desktop'))
1444		{
1445			// Lumina Desktop Environment
1446			$desktop_environment = 'Lumina';
1447			$desktop_version = str_replace('"', '', trim(shell_exec('lumina-desktop --version 2>&1')));
1448		}
1449		else if(pts_client::is_process_running('xfce4-session') || pts_client::is_process_running('xfce-mcs-manager') || $desktop_session == 'xfce')
1450		{
1451			// Xfce 4.x
1452			$desktop_environment = 'Xfce';
1453			$xfce_output = trim(shell_exec('xfce4-session-settings --version 2>&1'));
1454
1455			if(($open = strpos($xfce_output, '(Xfce')) > 0)
1456			{
1457				$xfce_output = substr($xfce_output, strpos($xfce_output, ' ', $open) + 1);
1458				$desktop_version = substr($xfce_output, 0, strpos($xfce_output, ')'));
1459			}
1460		}
1461		else if(pts_client::is_process_running('sugar-session'))
1462		{
1463			// Sugar Desktop Environment (namely for OLPC)
1464			$desktop_environment = 'Sugar';
1465			$desktop_version = null; // TODO: where can the Sugar version be figured out?
1466		}
1467		else if(pts_client::is_process_running('openbox'))
1468		{
1469			$desktop_environment = 'Openbox';
1470			$openbox_output = trim(shell_exec('openbox --version 2>&1'));
1471
1472			if(($openbox_d = stripos($openbox_output, 'Openbox ')) !== false)
1473			{
1474				$openbox_output = substr($openbox_output, ($openbox_d + 8));
1475				$desktop_version = substr($openbox_output, 0, strpos($openbox_output, PHP_EOL));
1476			}
1477		}
1478		else if(pts_client::is_process_running('cinnamon'))
1479		{
1480			$desktop_environment = 'Cinnamon';
1481			$desktop_version = pts_strings::last_in_string(trim(shell_exec('cinnamon --version 2> /dev/null')));
1482		}
1483		else if(pts_client::is_process_running('sway'))
1484		{
1485			$desktop_environment = 'Sway';
1486			$desktop_version = pts_strings::last_in_string(trim(shell_exec('sway --version 2> /dev/null')));
1487		}
1488		else if(pts_client::is_process_running('enlightenment'))
1489		{
1490			$desktop_environment = 'Enlightenment';
1491			$desktop_version = null; // No known -v / --version command on any Enlightenment component
1492		}
1493		else if(pts_client::is_process_running('consort-panel'))
1494		{
1495			$desktop_environment = 'Consort';
1496			$desktop_version = null; // TODO: Haven't tested Consort Desktop yet
1497		}
1498		else if(pts_client::is_process_running('razor-desktop'))
1499		{
1500			$desktop_environment = 'Razor-qt';
1501			$desktop_version = null; // TODO: Figure out how to determine razor version
1502		}
1503		else if(pts_client::is_process_running('icewm'))
1504		{
1505			$desktop_environment = 'IceWM';
1506			$desktop_version = null;
1507		}
1508		else if(pts_client::is_process_running('budgie-panel') || $desktop_session == 'budgie-desktop')
1509		{
1510			// Budgie
1511			$desktop_environment = 'Budgie';
1512		}
1513
1514		if(!empty($desktop_environment))
1515		{
1516			$desktop = $desktop_environment;
1517
1518			if(!empty($desktop_version) && pts_strings::is_version($desktop_version))
1519			{
1520				$desktop .= ' ' . $desktop_version;
1521			}
1522		}
1523
1524		return $desktop;
1525	}
1526	public static function sw_display_server()
1527	{
1528		$display_servers = array();
1529
1530		if(phodevi::is_windows())
1531		{
1532			// TODO: determine what to do for Windows support
1533		}
1534		else
1535		{
1536			if(pts_client::is_process_running('weston'))
1537			{
1538				$info = 'Wayland Weston';
1539				$vinfo = trim(shell_exec('weston --version 2>&1'));
1540
1541				if(pts_strings::last_in_string($vinfo) && pts_strings::is_version(pts_strings::last_in_string($vinfo)))
1542				{
1543					$info .= ' ' . pts_strings::last_in_string($vinfo);
1544				}
1545					array_push($display_servers, $info);
1546			}
1547			if(pts_client::is_process_running('unity-system-compositor'))
1548			{
1549				$unity_system_comp = trim(str_replace('unity-system-compositor', '', shell_exec('unity-system-compositor --version 2>&1')));
1550
1551				if(pts_strings::is_version($unity_system_comp))
1552				{
1553					array_push($display_servers, 'Unity-System-Compositor ' . $unity_system_comp);
1554				}
1555
1556			}
1557			$xorg_log = isset(phodevi::$vfs->xorg_log) ? phodevi::$vfs->xorg_log : false;
1558			if($xorg_log && ($x = strpos($xorg_log, 'X.Org X Server ')) !== false)
1559			{
1560				$xorg_log = substr($xorg_log, ($x + strlen('X.Org X Server ')));
1561				$xorg_log = substr($xorg_log, 0, strpos($xorg_log, PHP_EOL));
1562
1563				if(pts_strings::is_version($xorg_log))
1564				{
1565					array_push($display_servers, 'X Server ' . $xorg_log);
1566				}
1567			}
1568			else if(($x_bin = (is_executable('/usr/libexec/Xorg.bin') ? '/usr/libexec/Xorg.bin' : false)) || ($x_bin = pts_client::executable_in_path('Xorg')) || ($x_bin = pts_client::executable_in_path('X')))
1569			{
1570				// Find graphics subsystem version
1571				$info = shell_exec($x_bin . ' ' . (phodevi::is_solaris() ? ':0' : '') . ' -version 2>&1');
1572				$pos = (($p = strrpos($info, 'Release Date')) !== false ? $p : strrpos($info, 'Build Date'));
1573				$info = trim(substr($info, 0, $pos));
1574
1575				if($pos === false || getenv('DISPLAY') == false)
1576				{
1577					$version = null;
1578				}
1579				else if(($pos = strrpos($info, '(')) === false && strrpos($info, 'Server') === false)
1580				{
1581					$version = trim(substr($info, strrpos($info, ' ')));
1582				}
1583				else
1584				{
1585					$version = trim(substr($info, strrpos($info, 'Server') + 6));
1586
1587					if(!pts_strings::is_version($version))
1588					{
1589						$version = null;
1590					}
1591				}
1592
1593				array_push($display_servers, trim('X Server ' . $version));
1594			}
1595			if(pts_client::is_process_running('surfaceflinger'))
1596			{
1597				array_push($display_servers, 'SurfaceFlinger');
1598			}
1599
1600			if(pts_client::is_process_running('gnome-shell-wayland'))
1601			{
1602				array_push($display_servers, 'GNOME Shell Wayland');
1603			}
1604
1605			if(getenv('WAYLAND_DISPLAY') != false)
1606			{
1607				array_push($display_servers, 'Wayland');
1608			}
1609		}
1610
1611		return implode(' + ', $display_servers);
1612	}
1613	public static function sw_display_driver($with_version = true)
1614	{
1615		if(phodevi::is_windows())
1616		{
1617			$windows_driver = phodevi_windows_parser::get_wmi_object('Win32_VideoController', 'DriverVersion');
1618
1619			if(($nvidia_smi = pts_client::executable_in_path('nvidia-smi')))
1620			{
1621				$smi_output = shell_exec(escapeshellarg($nvidia_smi) . ' -q -d CLOCK');
1622				if(($v = stripos($smi_output, 'Driver Version')) !== false)
1623				{
1624					$nv_version = substr($smi_output, strpos($smi_output, ':', $v) + 1);
1625					$nv_version = trim(substr($nv_version, 0, strpos($nv_version, "\n")));
1626					if(pts_strings::is_version($nv_version))
1627					{
1628						$windows_driver = $nv_version . ' (' . $windows_driver . ')';
1629					}
1630				}
1631			}
1632			return $windows_driver;
1633		}
1634
1635		$display_driver = phodevi::read_property('system', 'dri-display-driver');
1636		$driver_version = null;
1637
1638		if(empty($display_driver) || $display_driver == 'NVIDIA')
1639		{
1640			if(phodevi::is_nvidia_graphics() || is_file('/proc/driver/nvidia/version'))
1641			{
1642				$display_driver = 'NVIDIA';
1643				if(($nvs_value = phodevi_parser::read_nvidia_extension('NvidiaDriverVersion')))
1644				{
1645					$driver_version = $nvs_value;
1646				}
1647				else
1648				{
1649					// NVIDIA's binary driver appends their driver version on the end of the OpenGL version string
1650					$glxinfo = phodevi_parser::software_glxinfo_version();
1651					if(($pos = strpos($glxinfo, 'NVIDIA ')) != false)
1652					{
1653						$driver_version = substr($glxinfo, ($pos + 7));
1654					}
1655				}
1656			}
1657			else if((phodevi::is_mesa_graphics() || phodevi::is_bsd()) && stripos(phodevi::read_property('gpu', 'model'), 'NVIDIA') !== false)
1658			{
1659				if(is_file('/sys/class/drm/version'))
1660				{
1661					// If there's DRM loaded and NVIDIA, it should be Nouveau
1662					$display_driver = 'nouveau';
1663				}
1664			}
1665		}
1666
1667		// XXX: As of PTS 10.2.1, commented out due to xorg logs growing too big on recent Ubuntu/bug causing slow perf
1668		if(false && !empty($display_driver))
1669		{
1670			$driver_version = phodevi_parser::read_xorg_module_version($display_driver . '_drv');
1671
1672			if($driver_version == false || $driver_version == '1.0.0')
1673			{
1674				switch($display_driver)
1675				{
1676					case 'amd':
1677						// See if it's radeon driver
1678						$driver_version = phodevi_parser::read_xorg_module_version('radeon_drv');
1679
1680						if($driver_version != false)
1681						{
1682							$display_driver = 'radeon';
1683						}
1684						// See if it's the newer AMDGPU driver
1685						$driver_version = phodevi_parser::read_xorg_module_version('amdgpu_drv');
1686
1687						if($driver_version != false)
1688						{
1689							$display_driver = 'amdgpu';
1690						}
1691						break;
1692					case 'vmwgfx':
1693						// See if it's VMware driver
1694						$driver_version = phodevi_parser::read_xorg_module_version('vmware_drv');
1695
1696						if($driver_version != false)
1697						{
1698							$display_driver = 'vmware';
1699						}
1700						break;
1701					case 'radeon':
1702						// RadeonHD driver also reports DRI driver as 'radeon', so try reading that instead
1703						$driver_version = phodevi_parser::read_xorg_module_version('radeonhd_drv');
1704
1705						if($driver_version != false)
1706						{
1707							$display_driver = 'radeonhd';
1708						}
1709						$driver_version = phodevi_parser::read_xorg_module_version('amdgpu_drv');
1710
1711						if($driver_version != false)
1712						{
1713							$display_driver = 'amdgpu';
1714						}
1715						break;
1716					case 'nvidia':
1717					case 'NVIDIA':
1718					case 'nouveau':
1719						// NVIDIA's binary driver usually ends up reporting 1.0.0
1720						if(($nvs_value = phodevi_parser::read_nvidia_extension('NvidiaDriverVersion')))
1721						{
1722							$display_driver = 'NVIDIA';
1723							$driver_version = $nvs_value;
1724						}
1725						else
1726						{
1727							// NVIDIA's binary driver appends their driver version on the end of the OpenGL version string
1728							$glxinfo = phodevi_parser::software_glxinfo_version();
1729
1730							if(($pos = strpos($glxinfo, 'NVIDIA ')) != false)
1731							{
1732								$display_driver = 'NVIDIA';
1733								$driver_version = substr($glxinfo, ($pos + 7));
1734							}
1735						}
1736						break;
1737					default:
1738						if(is_readable('/sys/class/graphics/fb0/name'))
1739						{
1740							// This path works for at least finding NVIDIA Tegra 2 DDX (via tegra_fb)
1741							$display_driver = file_get_contents('/sys/class/graphics/fb0/name');
1742							$display_driver = str_replace(array('drm', '_fb'), '', $display_driver);
1743							$driver_version = phodevi_parser::read_xorg_module_version($display_driver . '_drv');
1744						}
1745						break;
1746				}
1747			}
1748
1749			if($driver_version == false)
1750			{
1751				// If the version is empty, chances are the DDX driver string is incorrect
1752				$display_driver = null;
1753
1754				// See if the VESA or fbdev driver is in use
1755				foreach(array('modesetting', 'fbdev', 'vesa') as $drv)
1756				{
1757					$drv_version = phodevi_parser::read_xorg_module_version($drv . '_drv');
1758
1759					if($drv_version)
1760					{
1761						$display_driver = $drv;
1762						$driver_version = $drv_version;
1763						break;
1764					}
1765				}
1766			}
1767		}
1768		if(!empty($driver_version) && $with_version && $driver_version != '0.0.0')
1769		{
1770			$display_driver .= ' ' . $driver_version;
1771		}
1772
1773		return $display_driver;
1774	}
1775	public static function sw_opengl_driver()
1776	{
1777		// OpenGL version
1778		$info = null;
1779
1780		if(phodevi::is_windows())
1781		{
1782			$info = null; // TODO: Windows support
1783		}
1784		else if(pts_client::executable_in_path('nvidia-settings'))
1785		{
1786			$info = phodevi_parser::read_nvidia_extension('OpenGLVersion');
1787		}
1788
1789		if($info == null)
1790		{
1791			$info = phodevi_parser::software_glxinfo_version();
1792
1793			if($info && ($pos = strpos($info, ' ')) != false && strpos($info, 'Mesa') === false)
1794			{
1795				$info = substr($info, 0, $pos);
1796			}
1797
1798			$renderer = phodevi_parser::read_glx_renderer();
1799
1800			if($renderer && ($s = strpos($renderer, 'Gallium')) !== false)
1801			{
1802				$gallium = substr($renderer, $s);
1803				$gallium = substr($gallium, 0, strpos($gallium, ' ', strpos($gallium, '.')));
1804				$info .= ' ' . $gallium . '';
1805			}
1806
1807			if($renderer && ($s = strpos($renderer, 'LLVM ')) !== false)
1808			{
1809				$llvm = substr($renderer, $s);
1810				$llvm = substr($llvm, 0, strpos($llvm, ')'));
1811				$info .= ' (' . $llvm . ')';
1812			}
1813		}
1814
1815		return $info;
1816	}
1817	public static function sw_vulkan_driver()
1818	{
1819		// Vulkan driver/version
1820		$info = null;
1821
1822		if(isset(phodevi::$vfs->vulkaninfo))
1823		{
1824			if(($pos = strpos(phodevi::$vfs->vulkaninfo, 'Vulkan API Version:')) !== false)
1825			{
1826				$info = substr(phodevi::$vfs->vulkaninfo, $pos + 20);
1827				$info = trim(substr($info, 0, strpos($info, "\n")));
1828			}
1829			else if(($pos = strpos(phodevi::$vfs->vulkaninfo, 'apiVersion')) !== false)
1830			{
1831				$apiv = substr(phodevi::$vfs->vulkaninfo, $pos);
1832				$apiv = trim(substr($apiv, 0, strpos($apiv, ")\n")));
1833				$apiv = trim(substr($apiv, strpos($apiv, '(') + 1));
1834				if(pts_strings::is_version($apiv))
1835				{
1836					$info = $apiv;
1837				}
1838			}
1839		}
1840		/*
1841		if($info == null)
1842		{
1843			// A less than ideal fallback for some detection now
1844			foreach(array_merge(pts_file_io::glob('/etc/vulkan/icd.d/*.json'), pts_file_io::glob('/usr/share/vulkan/icd.d/*.json')) as $icd_json)
1845			{
1846				$icd_json = json_decode(file_get_contents($icd_json), true);
1847
1848				if(isset($icd_json['ICD']['api_version']) && !empty($icd_json['ICD']['api_version']))
1849				{
1850					$info = trim($icd_json['ICD']['api_version']);
1851					break;
1852				}
1853			}
1854		}
1855		*/
1856
1857		return $info;
1858	}
1859	public static function sw_opencl_driver()
1860	{
1861		// OpenCL driver/version
1862		$info = array();
1863
1864		if(isset(phodevi::$vfs->clinfo))
1865		{
1866			$sea = phodevi::$vfs->clinfo;
1867			while(($pos = strpos($sea, 'Platform Version')) != false)
1868			{
1869				$sea = substr($sea, $pos + 18);
1870				$info[] = trim(substr($sea, 0, strpos($sea, "\n")));
1871			}
1872		}
1873
1874		return implode(' + ', $info);
1875	}
1876	public static function sw_opengl_vendor()
1877	{
1878		// OpenGL version
1879		$info = null;
1880
1881		if(pts_client::executable_in_path('glxinfo'))
1882		{
1883			$info = shell_exec('glxinfo 2>&1 | grep vendor');
1884
1885			if($info && ($pos = strpos($info, 'OpenGL vendor string:')) !== false)
1886			{
1887				$info = substr($info, $pos + 22);
1888				$info = trim(substr($info, 0, strpos($info, "\n")));
1889			}
1890		}
1891		else if(is_readable('/dev/nvidia0'))
1892		{
1893			$info = 'NVIDIA';
1894		}
1895		else if(is_readable('/sys/module/fglrx/initstate') && pts_file_io::file_get_contents('/sys/module/fglrx/initstate') == 'live')
1896		{
1897			$info = 'ATI';
1898		}
1899		else if(is_readable('/dev/dri/card0'))
1900		{
1901			$info = 'Mesa';
1902		}
1903		else if(phodevi::is_bsd() && phodevi_bsd_parser::read_sysctl('dev.nvidia.0.%driver'))
1904		{
1905			$info = 'NVIDIA';
1906		}
1907
1908		return $info;
1909	}
1910	public static function sw_compiler_build_configuration($compiler)
1911	{
1912		$cc = shell_exec($compiler . ' -v 2>&1');
1913
1914		if(($t = stripos($cc, 'Configured with: ')) !== false)
1915		{
1916			$cc = substr($cc, ($t + 18));
1917			$cc = substr($cc, 0, strpos($cc, PHP_EOL));
1918			$cc = explode(' ', $cc);
1919			array_shift($cc); // this should just be the configure call (i.e. ../src/configure)
1920
1921			$drop_arguments = array(
1922				'--with-pkgversion=',
1923				'--with-bugurl=',
1924				'--prefix=',
1925				'--program-suffix=',
1926				'--libexecdir=',
1927				'--infodir=',
1928				'--libdir=',
1929				'--with-cloog=',
1930				'--with-isl=',
1931				'--with-java-home=',
1932				'--manddir=',
1933				'--with-ecj-jar=',
1934				'--with-jvm-jar-dir=',
1935				'--with-jvm-root-dir=',
1936				'--with-sysroot=',
1937				'--with-gxx-include-dir=',
1938				'--with-system-zlib',
1939				'--enable-linker-build-id',
1940				'--without-included-gettext'
1941				);
1942
1943			foreach($cc as $i => $argument)
1944			{
1945				$arg_length = strlen($argument);
1946				if($argument[0] != '-')
1947				{
1948					unset($cc[$i]);
1949				}
1950				else
1951				{
1952					foreach($drop_arguments as $check_to_drop)
1953					{
1954						$len = strlen($check_to_drop);
1955
1956						if($len <= $arg_length && substr($argument, 0, $len) == $check_to_drop)
1957						{
1958							unset($cc[$i]);
1959						}
1960					}
1961				}
1962			}
1963
1964			sort($cc);
1965			$cc = implode(' ', $cc);
1966		}
1967		else if(($t = stripos($cc, 'clang')) !== false)
1968		{
1969			$cc = null;
1970
1971			// Clang doesn't report "configured with" but has other useful tid-bits...
1972			if(($c = pts_client::executable_in_path('llvm-ld')) || ($c = pts_client::executable_in_path('llvm-link')))
1973			{
1974				$llvm_ld = shell_exec($c . ' -version 2>&1');
1975				/*
1976				EXAMPLE OUTPUT:
1977					LLVM (http://llvm.org/):
1978					  LLVM version 3.1svn
1979					  Optimized build.
1980					  Built Mar 23 2012 (08:53:34).
1981					  Default target: x86_64-unknown-linux-gnu
1982					  Host CPU: corei7-avx
1983				*/
1984
1985				if(stripos($llvm_ld, 'build') && (stripos($llvm_ld, 'host') || stripos($llvm_ld, 'target')))
1986				{
1987					$llvm_ld = explode(PHP_EOL, $llvm_ld);
1988
1989					if(stripos($llvm_ld[0], 'http://'))
1990					{
1991						array_shift($llvm_ld);
1992					}
1993					if(stripos($llvm_ld[0], 'version'))
1994					{
1995						array_shift($llvm_ld);
1996					}
1997
1998					foreach($llvm_ld as $i => &$line)
1999					{
2000						$line = trim($line);
2001						if(substr($line, -1) == '.')
2002						{
2003							$line = substr($line, 0, -1);
2004						}
2005
2006						if($line == null)
2007						{
2008							unset($llvm_ld[$i]);
2009						}
2010					}
2011
2012					$cc = implode('; ', $llvm_ld);
2013				}
2014			}
2015
2016		}
2017		else
2018		{
2019			$cc = null;
2020		}
2021
2022		return $cc;
2023	}
2024	public static function sw_dri_display_driver()
2025	{
2026		$dri_driver = false;
2027
2028		if(is_file('/proc/driver/nvidia/version'))
2029		{
2030			$dri_driver = 'nvidia';
2031		}
2032		else if(is_file('/sys/class/drm/card0/device/vendor'))
2033		{
2034			$vendor_id = pts_file_io::file_get_contents('/sys/class/drm/card0/device/vendor');
2035
2036			switch($vendor_id)
2037			{
2038				case 0x1002:
2039					$dri_driver = 'radeon';
2040					break;
2041				case 0x8086:
2042					$dri_driver = 'intel';
2043					break;
2044				case 0x10de:
2045					// NVIDIA
2046					$dri_driver = 'nouveau';
2047					break;
2048			}
2049		}
2050
2051		return $dri_driver;
2052	}
2053	public static function sw_java_version()
2054	{
2055		$java_version = trim(shell_exec('java -version 2>&1'));
2056
2057		if(strpos($java_version, 'not found') == false && (stripos($java_version, 'Java') !== false || stripos($java_version, 'jdk') !== false))
2058		{
2059			$java_version = explode("\n", $java_version);
2060
2061			if(($cut = count($java_version) - 2) > 0)
2062			{
2063				$v = $java_version[$cut];
2064			}
2065			else
2066			{
2067				$v = array_pop($java_version);
2068			}
2069
2070			$java_version = trim($v);
2071		}
2072		else
2073		{
2074			$java_version = null;
2075		}
2076
2077		return $java_version;
2078	}
2079	public static function sw_python_version()
2080	{
2081		$python_version = null;
2082
2083		if(($p = pts_client::executable_in_path('python')) != false)
2084		{
2085			$python_version = trim(shell_exec(escapeshellarg($p) . ' -V 2>&1'));
2086		}
2087		if(($p = pts_client::executable_in_path('python3')) != false)
2088		{
2089			$python3_version = trim(shell_exec(escapeshellarg($p) . ' -V 2>&1'));
2090			if($python3_version != $python_version)
2091			{
2092				$python_version .= ($python_version != null ? ' + ' : null) . $python3_version;
2093			}
2094		}
2095
2096		return $python_version;
2097	}
2098	public static function sw_wine_version()
2099	{
2100		$wine_version = null;
2101
2102		if((($use_wine = getenv('USE_WINE')) !== false || ($use_wine = getenv('WINE_VERSION')) !== false) && (is_executable($use_wine) || ($use_wine = pts_client::executable_in_path($use_wine)) !== false))
2103		{
2104			$wine_version = trim(shell_exec($use_wine . ' --version 2>&1'));
2105		}
2106		else if(pts_client::executable_in_path('wine') != false)
2107		{
2108			$wine_version = trim(shell_exec('wine --version 2>&1'));
2109		}
2110		else if(pts_client::executable_in_path('winecfg.exe') != false && pts_client::read_env('WINE_VERSION'))
2111		{
2112			$wine_version = trim(pts_client::read_env('WINE_VERSION'));
2113
2114			if(stripos($wine_version, 'wine') === false)
2115			{
2116				$wine_version = 'wine-' . $wine_version;
2117			}
2118		}
2119
2120		return $wine_version;
2121	}
2122	public static function battery()
2123	{
2124		$batteries = array();
2125		if(phodevi::is_linux())
2126		{
2127			foreach(pts_file_io::glob('/sys/class/power_supply/BAT*/model_name') as $bat_path)
2128			{
2129				$bat_model = pts_file_io::file_get_contents($bat_path);
2130				$bat_dir = dirname($bat_path);
2131				$bat_manufacturer = is_file($bat_dir . '/manufacturer') ? pts_file_io::file_get_contents($bat_dir . '/manufacturer') : null;
2132				if(!empty($bat_model))
2133				{
2134					$batteries[] = trim($bat_manufacturer . ' ' . $bat_model);
2135				}
2136			}
2137		}
2138
2139		return implode(' + ', $batteries);
2140	}
2141}
2142
2143?>
2144