We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Wine cannot find the FreeType font library
Origin doesn't render when "Prefer system libraries" is enabled.

![Screenshot from 2019-04-01 18-02-29](https://user-images.githubusercontent.com/15083452/55368762-7d9a1680-54a8-11e9-8633-dbd25e25fd48.png)

[lutris-origin-log.txt](https://github.com/lutris/lutris/files/3031911/lutris-origin-log.txt)

@aTosser 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lutris/util/linux.py`
Content:
```
1 """Linux specific platform code"""
2 import os
3 import shutil
4 import sys
5 import json
6 import platform
7 import resource
8 import subprocess
9 from collections import defaultdict
10 from lutris.vendor.distro import linux_distribution
11 from lutris.util.graphics import drivers
12 from lutris.util.graphics import glxinfo
13 from lutris.util.log import logger
14 from lutris.util.disks import get_drive_for_path
15 
16 SYSTEM_COMPONENTS = {
17     "COMMANDS": [
18         "xrandr",
19         "fuser",
20         "glxinfo",
21         "vulkaninfo",
22         "optirun",
23         "primusrun",
24         "pvkrun",
25         "xboxdrv",
26         "pulseaudio",
27         "lsi-steam",
28         "fuser",
29         "7z",
30         "gtk-update-icon-cache",
31         "lspci",
32         "xgamma",
33         "ldconfig",
34         "strangle",
35         "Xephyr",
36         "nvidia-smi",
37         "wine",
38         "fluidsynth",
39     ],
40     "TERMINALS": [
41         "xterm",
42         "gnome-terminal",
43         "konsole",
44         "xfce4-terminal",
45         "pantheon-terminal",
46         "terminator",
47         "mate-terminal",
48         "urxvt",
49         "cool-retro-term",
50         "Eterm",
51         "guake",
52         "lilyterm",
53         "lxterminal",
54         "roxterm",
55         "rxvt",
56         "aterm",
57         "sakura",
58         "st",
59         "terminology",
60         "termite",
61         "tilix",
62         "wterm",
63         "kitty",
64         "yuakuake",
65         "qterminal",
66     ],
67     "LIBRARIES": {
68         "OPENGL": [
69             "libGL.so.1",
70         ],
71         "VULKAN": [
72             "libvulkan.so.1",
73         ],
74         "WINE": [
75             "libsqlite3.so.0"
76         ],
77         "RADEON": [
78             "libvulkan_radeon.so"
79         ],
80         "GAMEMODE": [
81             "libgamemodeauto.so"
82         ]
83     }
84 }
85 
86 
87 class LinuxSystem:
88     """Global cache for system commands"""
89     _cache = {}
90 
91     soundfont_folders = [
92         '/usr/share/sounds/sf2',
93         '/usr/share/soundfonts',
94     ]
95 
96     recommended_no_file_open = 524288
97     required_components = ["OPENGL"]
98     optional_components = ["VULKAN", "WINE", "GAMEMODE"]
99 
100     def __init__(self):
101         for key in ("COMMANDS", "TERMINALS"):
102             self._cache[key] = {}
103             for command in SYSTEM_COMPONENTS[key]:
104                 command_path = shutil.which(command)
105                 if not command_path:
106                     command_path = self.get_sbin_path(command)
107                 if command_path:
108                     self._cache[key][command] = command_path
109 
110         # Detect if system is 64bit capable
111         self.is_64_bit = sys.maxsize > 2 ** 32
112         self.arch = self.get_arch()
113 
114         self.populate_libraries()
115         self.populate_sound_fonts()
116         self.soft_limit, self.hard_limit = self.get_file_limits()
117         self.glxinfo = self.get_glxinfo()
118 
119     @staticmethod
120     def get_sbin_path(command):
121         """Some distributions don't put sbin directories in $PATH"""
122         path_candidates = ["/sbin", "/usr/sbin"]
123         for candidate in path_candidates:
124             command_path = os.path.join(candidate, command)
125             if os.path.exists(command_path):
126                 return command_path
127 
128     @staticmethod
129     def get_file_limits():
130         return resource.getrlimit(resource.RLIMIT_NOFILE)
131 
132     def has_enough_file_descriptors(self):
133         return self.hard_limit >= self.recommended_no_file_open
134 
135     @staticmethod
136     def get_cpus():
137         """Parse the output of /proc/cpuinfo"""
138         cpus = [{}]
139         cpu_index = 0
140         with open("/proc/cpuinfo") as cpuinfo:
141             for line in cpuinfo.readlines():
142                 if not line.strip():
143                     cpu_index += 1
144                     cpus.append({})
145                     continue
146                 key, value = line.split(":", 1)
147                 cpus[cpu_index][key.strip()] = value.strip()
148         return [cpu for cpu in cpus if cpu]
149 
150     @staticmethod
151     def get_drives():
152         """Return a list of drives with their filesystems"""
153         try:
154             output = subprocess.check_output(["lsblk", "-f", "--json"]).decode()
155         except subprocess.CalledProcessError as ex:
156             logger.error("Failed to get drive information: %s", ex)
157             return None
158         return [
159             drive for drive in json.loads(output)["blockdevices"]
160             if drive["fstype"] != "squashfs"
161         ]
162 
163     @staticmethod
164     def get_ram_info():
165         """Return RAM information"""
166         try:
167             output = subprocess.check_output(["free"]).decode().split("\n")
168         except subprocess.CalledProcessError as ex:
169             logger.error("Failed to get RAM information: %s", ex)
170             return None
171         columns = output[0].split()
172         meminfo = {}
173         for parts in [line.split() for line in output[1:] if line]:
174             meminfo[parts[0].strip(":").lower()] = dict(zip(columns, parts[1:]))
175         return meminfo
176 
177     @staticmethod
178     def get_dist_info():
179         """Return distribution information"""
180         return linux_distribution()
181 
182     @staticmethod
183     def get_arch():
184         """Return the system architecture only if compatible
185         with the supported architectures from the Lutris API
186         """
187         machine = platform.machine()
188         if "64" in machine:
189             return "x86_64"
190         if "86" in machine:
191             return "i386"
192         if "armv7" in machine:
193             return "armv7"
194         logger.warning("Unsupported architecture %s", machine)
195 
196     @property
197     def runtime_architectures(self):
198         if self.arch == "x86_64":
199             return ["i386", "x86_64"]
200         return ["i386"]
201 
202     @property
203     def requirements(self):
204         return self.get_requirements()
205 
206     @property
207     def critical_requirements(self):
208         return self.get_requirements(include_optional=False)
209 
210     @property
211     def lib_folders(self):
212         return self.get_lib_folders()
213 
214     def get_fs_type_for_path(self, path):
215         """Return the filesystem type a given path uses"""
216         path_drive = get_drive_for_path(path)
217         for drive in self.get_drives():
218             for partition in drive.get("children", []):
219                 if "/dev/%s" % partition["name"] == path_drive:
220                     return partition["fstype"]
221 
222     def get_lib_folders(self):
223         # Use ldconfig to locate the correct locations for system libs.
224         _paths = [[], []]
225         _candidates = (
226             subprocess.Popen([self.get("ldconfig"), '-p'],
227                              stdout=subprocess.PIPE,
228                              universal_newlines=True)
229         ).communicate()[0].split('\n')
230         for req in self.requirements:
231             for candidate in _candidates:
232                 for lib in SYSTEM_COMPONENTS["LIBRARIES"][req]:
233                     if lib in candidate:
234                         if 'x86-64' in candidate:
235                             candidate = candidate.split(' => ')[1].split(lib)[0]
236                             if candidate not in _paths[1]:
237                                 _paths[1].append(candidate)
238                         else:
239                             candidate = candidate.split(' => ')[1].split(lib)[0]
240                             if candidate not in _paths[0]:
241                                 _paths[0].append(candidate)
242         return _paths
243 
244     def get_glxinfo(self):
245         """Return a GlxInfo instance if the gfxinfo tool is available"""
246         if not self.get("glxinfo"):
247             return
248         _glxinfo = glxinfo.GlxInfo()
249         if not hasattr(_glxinfo, "display"):
250             logger.warning("Invalid glxinfo received")
251             return
252         return _glxinfo
253 
254     def get_requirements(self, include_optional=True):
255         """Return used system requirements"""
256         _requirements = self.required_components.copy()
257         if include_optional:
258             _requirements += self.optional_components
259             if drivers.is_amd():
260                 _requirements.append("RADEON")
261         return _requirements
262 
263     def get(self, command):
264         """Return a system command path if available"""
265         return self._cache["COMMANDS"].get(command)
266 
267     def get_terminals(self):
268         """Return list of installed terminals"""
269         return list(self._cache["TERMINALS"].values())
270 
271     def get_soundfonts(self):
272         """Return path of available soundfonts"""
273         return self._cache["SOUNDFONTS"]
274 
275     def iter_lib_folders(self):
276         """Loop over existing 32/64 bit library folders"""
277         for lib_paths in self.lib_folders:
278             if self.arch != 'x86_64':
279                 # On non amd64 setups, only the first element is relevant
280                 lib_paths = [lib_paths[0]]
281             yield lib_paths
282 
283     def populate_libraries(self):
284         """Populates the LIBRARIES cache with what is found on the system"""
285         self._cache["LIBRARIES"] = {}
286         for arch in self.runtime_architectures:
287             self._cache["LIBRARIES"][arch] = defaultdict(list)
288         for lib_paths in self.iter_lib_folders():
289             for path in lib_paths:
290                 for req in self.requirements:
291                     for lib in SYSTEM_COMPONENTS["LIBRARIES"][req]:
292                         for index, arch in enumerate(self.runtime_architectures):
293                             if os.path.exists(os.path.join(path, lib)):
294                                 self._cache["LIBRARIES"][arch][req].append(lib)
295 
296     def populate_sound_fonts(self):
297         """Populates the soundfont cache"""
298         self._cache["SOUNDFONTS"] = []
299         for folder in self.soundfont_folders:
300             if not os.path.exists(folder):
301                 continue
302             for soundfont in os.listdir(folder):
303                 self._cache["SOUNDFONTS"].append(soundfont)
304 
305     def get_missing_requirement_libs(self, req):
306         """Return a list of sets of missing libraries for each supported architecture"""
307         required_libs = set(SYSTEM_COMPONENTS["LIBRARIES"][req])
308         return [
309             list(required_libs - set(self._cache["LIBRARIES"][arch][req]))
310             for arch in self.runtime_architectures
311         ]
312 
313     def get_missing_libs(self):
314         """Return a dictionary of missing libraries"""
315         return {
316             req: self.get_missing_requirement_libs(req)
317             for req in self.requirements
318         }
319 
320     def is_feature_supported(self, feature):
321         """Return whether the system has the necessary libs to support a feature"""
322         return not self.get_missing_requirement_libs(feature)[0]
323 
324 
325 LINUX_SYSTEM = LinuxSystem()
326 
327 
328 def gather_system_info():
329     """Get all system information in a single data structure"""
330     system_info = {}
331     if drivers.is_nvidia():
332         system_info["nvidia_driver"] = drivers.get_nvidia_driver_info()
333         system_info["nvidia_gpus"] = [
334             drivers.get_nvidia_gpu_info(gpu_id)
335             for gpu_id in drivers.get_nvidia_gpu_ids()
336         ]
337     system_info["gpus"] = [drivers.get_gpu_info(gpu) for gpu in drivers.get_gpus()]
338     system_info["env"] = dict(os.environ)
339     system_info["missing_libs"] = LINUX_SYSTEM.get_missing_libs()
340     system_info["cpus"] = LINUX_SYSTEM.get_cpus()
341     system_info["drives"] = LINUX_SYSTEM.get_drives()
342     system_info["ram"] = LINUX_SYSTEM.get_ram_info()
343     system_info["dist"] = LINUX_SYSTEM.get_dist_info()
344     system_info["glxinfo"] = glxinfo.GlxInfo().as_dict()
345     return system_info
346
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lutris/util/linux.py b/lutris/util/linux.py
--- a/lutris/util/linux.py
+++ b/lutris/util/linux.py
@@ -220,27 +220,26 @@
                     return partition["fstype"]
 
     def get_lib_folders(self):
-        # Use ldconfig to locate the correct locations for system libs.
-        _paths = [[], []]
-        _candidates = (
-            subprocess.Popen([self.get("ldconfig"), '-p'],
-                             stdout=subprocess.PIPE,
-                             universal_newlines=True)
-        ).communicate()[0].split('\n')
-        for req in self.requirements:
-            for candidate in _candidates:
-                for lib in SYSTEM_COMPONENTS["LIBRARIES"][req]:
-                    if lib in candidate:
-                        if 'x86-64' in candidate:
-                            candidate = candidate.split(' => ')[1].split(lib)[0]
-                            if candidate not in _paths[1]:
-                                _paths[1].append(candidate)
-                        else:
-                            candidate = candidate.split(' => ')[1].split(lib)[0]
-                            if candidate not in _paths[0]:
-                                _paths[0].append(candidate)
+        # Use ldconfig to locate the correct locations for system libs. Sorting is done to preserve ordering for the distros that care.
+        _cand_dict = {}
+        for candidate in subprocess.run([self.get("ldconfig"), '-p'], stdout=subprocess.PIPE, universal_newlines=True).stdout.split('\n'):
+            if '=>' in candidate:
+                candidate = candidate.split(' => ')
+                if candidate[0] not in _cand_dict:
+                    _cand_dict.update({candidate[0]: [candidate[1].rsplit('/', 1)[0]]})
+                else:
+                    _cand_dict[candidate[0]].append(candidate[1].rsplit('/', 1)[0])
+        _paths = [[],[]]
+        for candidate, path in sorted(_cand_dict.items(), key = lambda cand: len(cand[1]), reverse = True):
+            if 'x86-64' in candidate:
+                if  path[0] not in _paths[1]:
+                    _paths[1].append(path[0])
+            elif 'libc' in candidate:
+                if path[0] not in _paths[0]:
+                    _paths[0].append(path[0])
         return _paths
 
+
     def get_glxinfo(self):
         """Return a GlxInfo instance if the gfxinfo tool is available"""
         if not self.get("glxinfo"):

{"golden_diff": "diff --git a/lutris/util/linux.py b/lutris/util/linux.py\n--- a/lutris/util/linux.py\n+++ b/lutris/util/linux.py\n@@ -220,27 +220,26 @@\n                     return partition[\"fstype\"]\n \n     def get_lib_folders(self):\n-        # Use ldconfig to locate the correct locations for system libs.\n-        _paths = [[], []]\n-        _candidates = (\n-            subprocess.Popen([self.get(\"ldconfig\"), '-p'],\n-                             stdout=subprocess.PIPE,\n-                             universal_newlines=True)\n-        ).communicate()[0].split('\\n')\n-        for req in self.requirements:\n-            for candidate in _candidates:\n-                for lib in SYSTEM_COMPONENTS[\"LIBRARIES\"][req]:\n-                    if lib in candidate:\n-                        if 'x86-64' in candidate:\n-                            candidate = candidate.split(' => ')[1].split(lib)[0]\n-                            if candidate not in _paths[1]:\n-                                _paths[1].append(candidate)\n-                        else:\n-                            candidate = candidate.split(' => ')[1].split(lib)[0]\n-                            if candidate not in _paths[0]:\n-                                _paths[0].append(candidate)\n+        # Use ldconfig to locate the correct locations for system libs. Sorting is done to preserve ordering for the distros that care.\n+        _cand_dict = {}\n+        for candidate in subprocess.run([self.get(\"ldconfig\"), '-p'], stdout=subprocess.PIPE, universal_newlines=True).stdout.split('\\n'):\n+            if '=>' in candidate:\n+                candidate = candidate.split(' => ')\n+                if candidate[0] not in _cand_dict:\n+                    _cand_dict.update({candidate[0]: [candidate[1].rsplit('/', 1)[0]]})\n+                else:\n+                    _cand_dict[candidate[0]].append(candidate[1].rsplit('/', 1)[0])\n+        _paths = [[],[]]\n+        for candidate, path in sorted(_cand_dict.items(), key = lambda cand: len(cand[1]), reverse = True):\n+            if 'x86-64' in candidate:\n+                if  path[0] not in _paths[1]:\n+                    _paths[1].append(path[0])\n+            elif 'libc' in candidate:\n+                if path[0] not in _paths[0]:\n+                    _paths[0].append(path[0])\n         return _paths\n \n+\n     def get_glxinfo(self):\n         \"\"\"Return a GlxInfo instance if the gfxinfo tool is available\"\"\"\n         if not self.get(\"glxinfo\"):\n", "issue": "Wine cannot find the FreeType font library\nOrigin doesn't render when \"Prefer system libraries\" is enabled.\r\n\r\n![Screenshot from 2019-04-01 18-02-29](https://user-images.githubusercontent.com/15083452/55368762-7d9a1680-54a8-11e9-8633-dbd25e25fd48.png)\r\n\r\n[lutris-origin-log.txt](https://github.com/lutris/lutris/files/3031911/lutris-origin-log.txt)\r\n\r\n@aTosser \n", "before_files": [{"content": "\"\"\"Linux specific platform code\"\"\"\nimport os\nimport shutil\nimport sys\nimport json\nimport platform\nimport resource\nimport subprocess\nfrom collections import defaultdict\nfrom lutris.vendor.distro import linux_distribution\nfrom lutris.util.graphics import drivers\nfrom lutris.util.graphics import glxinfo\nfrom lutris.util.log import logger\nfrom lutris.util.disks import get_drive_for_path\n\nSYSTEM_COMPONENTS = {\n    \"COMMANDS\": [\n        \"xrandr\",\n        \"fuser\",\n        \"glxinfo\",\n        \"vulkaninfo\",\n        \"optirun\",\n        \"primusrun\",\n        \"pvkrun\",\n        \"xboxdrv\",\n        \"pulseaudio\",\n        \"lsi-steam\",\n        \"fuser\",\n        \"7z\",\n        \"gtk-update-icon-cache\",\n        \"lspci\",\n        \"xgamma\",\n        \"ldconfig\",\n        \"strangle\",\n        \"Xephyr\",\n        \"nvidia-smi\",\n        \"wine\",\n        \"fluidsynth\",\n    ],\n    \"TERMINALS\": [\n        \"xterm\",\n        \"gnome-terminal\",\n        \"konsole\",\n        \"xfce4-terminal\",\n        \"pantheon-terminal\",\n        \"terminator\",\n        \"mate-terminal\",\n        \"urxvt\",\n        \"cool-retro-term\",\n        \"Eterm\",\n        \"guake\",\n        \"lilyterm\",\n        \"lxterminal\",\n        \"roxterm\",\n        \"rxvt\",\n        \"aterm\",\n        \"sakura\",\n        \"st\",\n        \"terminology\",\n        \"termite\",\n        \"tilix\",\n        \"wterm\",\n        \"kitty\",\n        \"yuakuake\",\n        \"qterminal\",\n    ],\n    \"LIBRARIES\": {\n        \"OPENGL\": [\n            \"libGL.so.1\",\n        ],\n        \"VULKAN\": [\n            \"libvulkan.so.1\",\n        ],\n        \"WINE\": [\n            \"libsqlite3.so.0\"\n        ],\n        \"RADEON\": [\n            \"libvulkan_radeon.so\"\n        ],\n        \"GAMEMODE\": [\n            \"libgamemodeauto.so\"\n        ]\n    }\n}\n\n\nclass LinuxSystem:\n    \"\"\"Global cache for system commands\"\"\"\n    _cache = {}\n\n    soundfont_folders = [\n        '/usr/share/sounds/sf2',\n        '/usr/share/soundfonts',\n    ]\n\n    recommended_no_file_open = 524288\n    required_components = [\"OPENGL\"]\n    optional_components = [\"VULKAN\", \"WINE\", \"GAMEMODE\"]\n\n    def __init__(self):\n        for key in (\"COMMANDS\", \"TERMINALS\"):\n            self._cache[key] = {}\n            for command in SYSTEM_COMPONENTS[key]:\n                command_path = shutil.which(command)\n                if not command_path:\n                    command_path = self.get_sbin_path(command)\n                if command_path:\n                    self._cache[key][command] = command_path\n\n        # Detect if system is 64bit capable\n        self.is_64_bit = sys.maxsize > 2 ** 32\n        self.arch = self.get_arch()\n\n        self.populate_libraries()\n        self.populate_sound_fonts()\n        self.soft_limit, self.hard_limit = self.get_file_limits()\n        self.glxinfo = self.get_glxinfo()\n\n    @staticmethod\n    def get_sbin_path(command):\n        \"\"\"Some distributions don't put sbin directories in $PATH\"\"\"\n        path_candidates = [\"/sbin\", \"/usr/sbin\"]\n        for candidate in path_candidates:\n            command_path = os.path.join(candidate, command)\n            if os.path.exists(command_path):\n                return command_path\n\n    @staticmethod\n    def get_file_limits():\n        return resource.getrlimit(resource.RLIMIT_NOFILE)\n\n    def has_enough_file_descriptors(self):\n        return self.hard_limit >= self.recommended_no_file_open\n\n    @staticmethod\n    def get_cpus():\n        \"\"\"Parse the output of /proc/cpuinfo\"\"\"\n        cpus = [{}]\n        cpu_index = 0\n        with open(\"/proc/cpuinfo\") as cpuinfo:\n            for line in cpuinfo.readlines():\n                if not line.strip():\n                    cpu_index += 1\n                    cpus.append({})\n                    continue\n                key, value = line.split(\":\", 1)\n                cpus[cpu_index][key.strip()] = value.strip()\n        return [cpu for cpu in cpus if cpu]\n\n    @staticmethod\n    def get_drives():\n        \"\"\"Return a list of drives with their filesystems\"\"\"\n        try:\n            output = subprocess.check_output([\"lsblk\", \"-f\", \"--json\"]).decode()\n        except subprocess.CalledProcessError as ex:\n            logger.error(\"Failed to get drive information: %s\", ex)\n            return None\n        return [\n            drive for drive in json.loads(output)[\"blockdevices\"]\n            if drive[\"fstype\"] != \"squashfs\"\n        ]\n\n    @staticmethod\n    def get_ram_info():\n        \"\"\"Return RAM information\"\"\"\n        try:\n            output = subprocess.check_output([\"free\"]).decode().split(\"\\n\")\n        except subprocess.CalledProcessError as ex:\n            logger.error(\"Failed to get RAM information: %s\", ex)\n            return None\n        columns = output[0].split()\n        meminfo = {}\n        for parts in [line.split() for line in output[1:] if line]:\n            meminfo[parts[0].strip(\":\").lower()] = dict(zip(columns, parts[1:]))\n        return meminfo\n\n    @staticmethod\n    def get_dist_info():\n        \"\"\"Return distribution information\"\"\"\n        return linux_distribution()\n\n    @staticmethod\n    def get_arch():\n        \"\"\"Return the system architecture only if compatible\n        with the supported architectures from the Lutris API\n        \"\"\"\n        machine = platform.machine()\n        if \"64\" in machine:\n            return \"x86_64\"\n        if \"86\" in machine:\n            return \"i386\"\n        if \"armv7\" in machine:\n            return \"armv7\"\n        logger.warning(\"Unsupported architecture %s\", machine)\n\n    @property\n    def runtime_architectures(self):\n        if self.arch == \"x86_64\":\n            return [\"i386\", \"x86_64\"]\n        return [\"i386\"]\n\n    @property\n    def requirements(self):\n        return self.get_requirements()\n\n    @property\n    def critical_requirements(self):\n        return self.get_requirements(include_optional=False)\n\n    @property\n    def lib_folders(self):\n        return self.get_lib_folders()\n\n    def get_fs_type_for_path(self, path):\n        \"\"\"Return the filesystem type a given path uses\"\"\"\n        path_drive = get_drive_for_path(path)\n        for drive in self.get_drives():\n            for partition in drive.get(\"children\", []):\n                if \"/dev/%s\" % partition[\"name\"] == path_drive:\n                    return partition[\"fstype\"]\n\n    def get_lib_folders(self):\n        # Use ldconfig to locate the correct locations for system libs.\n        _paths = [[], []]\n        _candidates = (\n            subprocess.Popen([self.get(\"ldconfig\"), '-p'],\n                             stdout=subprocess.PIPE,\n                             universal_newlines=True)\n        ).communicate()[0].split('\\n')\n        for req in self.requirements:\n            for candidate in _candidates:\n                for lib in SYSTEM_COMPONENTS[\"LIBRARIES\"][req]:\n                    if lib in candidate:\n                        if 'x86-64' in candidate:\n                            candidate = candidate.split(' => ')[1].split(lib)[0]\n                            if candidate not in _paths[1]:\n                                _paths[1].append(candidate)\n                        else:\n                            candidate = candidate.split(' => ')[1].split(lib)[0]\n                            if candidate not in _paths[0]:\n                                _paths[0].append(candidate)\n        return _paths\n\n    def get_glxinfo(self):\n        \"\"\"Return a GlxInfo instance if the gfxinfo tool is available\"\"\"\n        if not self.get(\"glxinfo\"):\n            return\n        _glxinfo = glxinfo.GlxInfo()\n        if not hasattr(_glxinfo, \"display\"):\n            logger.warning(\"Invalid glxinfo received\")\n            return\n        return _glxinfo\n\n    def get_requirements(self, include_optional=True):\n        \"\"\"Return used system requirements\"\"\"\n        _requirements = self.required_components.copy()\n        if include_optional:\n            _requirements += self.optional_components\n            if drivers.is_amd():\n                _requirements.append(\"RADEON\")\n        return _requirements\n\n    def get(self, command):\n        \"\"\"Return a system command path if available\"\"\"\n        return self._cache[\"COMMANDS\"].get(command)\n\n    def get_terminals(self):\n        \"\"\"Return list of installed terminals\"\"\"\n        return list(self._cache[\"TERMINALS\"].values())\n\n    def get_soundfonts(self):\n        \"\"\"Return path of available soundfonts\"\"\"\n        return self._cache[\"SOUNDFONTS\"]\n\n    def iter_lib_folders(self):\n        \"\"\"Loop over existing 32/64 bit library folders\"\"\"\n        for lib_paths in self.lib_folders:\n            if self.arch != 'x86_64':\n                # On non amd64 setups, only the first element is relevant\n                lib_paths = [lib_paths[0]]\n            yield lib_paths\n\n    def populate_libraries(self):\n        \"\"\"Populates the LIBRARIES cache with what is found on the system\"\"\"\n        self._cache[\"LIBRARIES\"] = {}\n        for arch in self.runtime_architectures:\n            self._cache[\"LIBRARIES\"][arch] = defaultdict(list)\n        for lib_paths in self.iter_lib_folders():\n            for path in lib_paths:\n                for req in self.requirements:\n                    for lib in SYSTEM_COMPONENTS[\"LIBRARIES\"][req]:\n                        for index, arch in enumerate(self.runtime_architectures):\n                            if os.path.exists(os.path.join(path, lib)):\n                                self._cache[\"LIBRARIES\"][arch][req].append(lib)\n\n    def populate_sound_fonts(self):\n        \"\"\"Populates the soundfont cache\"\"\"\n        self._cache[\"SOUNDFONTS\"] = []\n        for folder in self.soundfont_folders:\n            if not os.path.exists(folder):\n                continue\n            for soundfont in os.listdir(folder):\n                self._cache[\"SOUNDFONTS\"].append(soundfont)\n\n    def get_missing_requirement_libs(self, req):\n        \"\"\"Return a list of sets of missing libraries for each supported architecture\"\"\"\n        required_libs = set(SYSTEM_COMPONENTS[\"LIBRARIES\"][req])\n        return [\n            list(required_libs - set(self._cache[\"LIBRARIES\"][arch][req]))\n            for arch in self.runtime_architectures\n        ]\n\n    def get_missing_libs(self):\n        \"\"\"Return a dictionary of missing libraries\"\"\"\n        return {\n            req: self.get_missing_requirement_libs(req)\n            for req in self.requirements\n        }\n\n    def is_feature_supported(self, feature):\n        \"\"\"Return whether the system has the necessary libs to support a feature\"\"\"\n        return not self.get_missing_requirement_libs(feature)[0]\n\n\nLINUX_SYSTEM = LinuxSystem()\n\n\ndef gather_system_info():\n    \"\"\"Get all system information in a single data structure\"\"\"\n    system_info = {}\n    if drivers.is_nvidia():\n        system_info[\"nvidia_driver\"] = drivers.get_nvidia_driver_info()\n        system_info[\"nvidia_gpus\"] = [\n            drivers.get_nvidia_gpu_info(gpu_id)\n            for gpu_id in drivers.get_nvidia_gpu_ids()\n        ]\n    system_info[\"gpus\"] = [drivers.get_gpu_info(gpu) for gpu in drivers.get_gpus()]\n    system_info[\"env\"] = dict(os.environ)\n    system_info[\"missing_libs\"] = LINUX_SYSTEM.get_missing_libs()\n    system_info[\"cpus\"] = LINUX_SYSTEM.get_cpus()\n    system_info[\"drives\"] = LINUX_SYSTEM.get_drives()\n    system_info[\"ram\"] = LINUX_SYSTEM.get_ram_info()\n    system_info[\"dist\"] = LINUX_SYSTEM.get_dist_info()\n    system_info[\"glxinfo\"] = glxinfo.GlxInfo().as_dict()\n    return system_info\n", "path": "lutris/util/linux.py"}], "after_files": [{"content": "\"\"\"Linux specific platform code\"\"\"\nimport os\nimport shutil\nimport sys\nimport json\nimport platform\nimport resource\nimport subprocess\nfrom collections import defaultdict\nfrom lutris.vendor.distro import linux_distribution\nfrom lutris.util.graphics import drivers\nfrom lutris.util.graphics import glxinfo\nfrom lutris.util.log import logger\nfrom lutris.util.disks import get_drive_for_path\n\nSYSTEM_COMPONENTS = {\n    \"COMMANDS\": [\n        \"xrandr\",\n        \"fuser\",\n        \"glxinfo\",\n        \"vulkaninfo\",\n        \"optirun\",\n        \"primusrun\",\n        \"pvkrun\",\n        \"xboxdrv\",\n        \"pulseaudio\",\n        \"lsi-steam\",\n        \"fuser\",\n        \"7z\",\n        \"gtk-update-icon-cache\",\n        \"lspci\",\n        \"xgamma\",\n        \"ldconfig\",\n        \"strangle\",\n        \"Xephyr\",\n        \"nvidia-smi\",\n        \"wine\",\n        \"fluidsynth\",\n    ],\n    \"TERMINALS\": [\n        \"xterm\",\n        \"gnome-terminal\",\n        \"konsole\",\n        \"xfce4-terminal\",\n        \"pantheon-terminal\",\n        \"terminator\",\n        \"mate-terminal\",\n        \"urxvt\",\n        \"cool-retro-term\",\n        \"Eterm\",\n        \"guake\",\n        \"lilyterm\",\n        \"lxterminal\",\n        \"roxterm\",\n        \"rxvt\",\n        \"aterm\",\n        \"sakura\",\n        \"st\",\n        \"terminology\",\n        \"termite\",\n        \"tilix\",\n        \"wterm\",\n        \"kitty\",\n        \"yuakuake\",\n        \"qterminal\",\n    ],\n    \"LIBRARIES\": {\n        \"OPENGL\": [\n            \"libGL.so.1\",\n        ],\n        \"VULKAN\": [\n            \"libvulkan.so.1\",\n        ],\n        \"WINE\": [\n            \"libsqlite3.so.0\"\n        ],\n        \"RADEON\": [\n            \"libvulkan_radeon.so\"\n        ],\n        \"GAMEMODE\": [\n            \"libgamemodeauto.so\"\n        ]\n    }\n}\n\n\nclass LinuxSystem:\n    \"\"\"Global cache for system commands\"\"\"\n    _cache = {}\n\n    soundfont_folders = [\n        '/usr/share/sounds/sf2',\n        '/usr/share/soundfonts',\n    ]\n\n    recommended_no_file_open = 524288\n    required_components = [\"OPENGL\"]\n    optional_components = [\"VULKAN\", \"WINE\", \"GAMEMODE\"]\n\n    def __init__(self):\n        for key in (\"COMMANDS\", \"TERMINALS\"):\n            self._cache[key] = {}\n            for command in SYSTEM_COMPONENTS[key]:\n                command_path = shutil.which(command)\n                if not command_path:\n                    command_path = self.get_sbin_path(command)\n                if command_path:\n                    self._cache[key][command] = command_path\n\n        # Detect if system is 64bit capable\n        self.is_64_bit = sys.maxsize > 2 ** 32\n        self.arch = self.get_arch()\n\n        self.populate_libraries()\n        self.populate_sound_fonts()\n        self.soft_limit, self.hard_limit = self.get_file_limits()\n        self.glxinfo = self.get_glxinfo()\n\n    @staticmethod\n    def get_sbin_path(command):\n        \"\"\"Some distributions don't put sbin directories in $PATH\"\"\"\n        path_candidates = [\"/sbin\", \"/usr/sbin\"]\n        for candidate in path_candidates:\n            command_path = os.path.join(candidate, command)\n            if os.path.exists(command_path):\n                return command_path\n\n    @staticmethod\n    def get_file_limits():\n        return resource.getrlimit(resource.RLIMIT_NOFILE)\n\n    def has_enough_file_descriptors(self):\n        return self.hard_limit >= self.recommended_no_file_open\n\n    @staticmethod\n    def get_cpus():\n        \"\"\"Parse the output of /proc/cpuinfo\"\"\"\n        cpus = [{}]\n        cpu_index = 0\n        with open(\"/proc/cpuinfo\") as cpuinfo:\n            for line in cpuinfo.readlines():\n                if not line.strip():\n                    cpu_index += 1\n                    cpus.append({})\n                    continue\n                key, value = line.split(\":\", 1)\n                cpus[cpu_index][key.strip()] = value.strip()\n        return [cpu for cpu in cpus if cpu]\n\n    @staticmethod\n    def get_drives():\n        \"\"\"Return a list of drives with their filesystems\"\"\"\n        try:\n            output = subprocess.check_output([\"lsblk\", \"-f\", \"--json\"]).decode()\n        except subprocess.CalledProcessError as ex:\n            logger.error(\"Failed to get drive information: %s\", ex)\n            return None\n        return [\n            drive for drive in json.loads(output)[\"blockdevices\"]\n            if drive[\"fstype\"] != \"squashfs\"\n        ]\n\n    @staticmethod\n    def get_ram_info():\n        \"\"\"Return RAM information\"\"\"\n        try:\n            output = subprocess.check_output([\"free\"]).decode().split(\"\\n\")\n        except subprocess.CalledProcessError as ex:\n            logger.error(\"Failed to get RAM information: %s\", ex)\n            return None\n        columns = output[0].split()\n        meminfo = {}\n        for parts in [line.split() for line in output[1:] if line]:\n            meminfo[parts[0].strip(\":\").lower()] = dict(zip(columns, parts[1:]))\n        return meminfo\n\n    @staticmethod\n    def get_dist_info():\n        \"\"\"Return distribution information\"\"\"\n        return linux_distribution()\n\n    @staticmethod\n    def get_arch():\n        \"\"\"Return the system architecture only if compatible\n        with the supported architectures from the Lutris API\n        \"\"\"\n        machine = platform.machine()\n        if \"64\" in machine:\n            return \"x86_64\"\n        if \"86\" in machine:\n            return \"i386\"\n        if \"armv7\" in machine:\n            return \"armv7\"\n        logger.warning(\"Unsupported architecture %s\", machine)\n\n    @property\n    def runtime_architectures(self):\n        if self.arch == \"x86_64\":\n            return [\"i386\", \"x86_64\"]\n        return [\"i386\"]\n\n    @property\n    def requirements(self):\n        return self.get_requirements()\n\n    @property\n    def critical_requirements(self):\n        return self.get_requirements(include_optional=False)\n\n    @property\n    def lib_folders(self):\n        return self.get_lib_folders()\n\n    def get_fs_type_for_path(self, path):\n        \"\"\"Return the filesystem type a given path uses\"\"\"\n        path_drive = get_drive_for_path(path)\n        for drive in self.get_drives():\n            for partition in drive.get(\"children\", []):\n                if \"/dev/%s\" % partition[\"name\"] == path_drive:\n                    return partition[\"fstype\"]\n\n    def get_lib_folders(self):\n        # Use ldconfig to locate the correct locations for system libs. Sorting is done to preserve ordering for the distros that care.\n        _cand_dict = {}\n        for candidate in subprocess.run([self.get(\"ldconfig\"), '-p'], stdout=subprocess.PIPE, universal_newlines=True).stdout.split('\\n'):\n            if '=>' in candidate:\n                candidate = candidate.split(' => ')\n                if candidate[0] not in _cand_dict:\n                    _cand_dict.update({candidate[0]: [candidate[1].rsplit('/', 1)[0]]})\n                else:\n                    _cand_dict[candidate[0]].append(candidate[1].rsplit('/', 1)[0])\n        _paths = [[],[]]\n        for candidate, path in sorted(_cand_dict.items(), key = lambda cand: len(cand[1]), reverse = True):\n            if 'x86-64' in candidate:\n                if  path[0] not in _paths[1]:\n                    _paths[1].append(path[0])\n            elif 'libc' in candidate:\n                if path[0] not in _paths[0]:\n                    _paths[0].append(path[0])\n        return _paths\n\n\n    def get_glxinfo(self):\n        \"\"\"Return a GlxInfo instance if the gfxinfo tool is available\"\"\"\n        if not self.get(\"glxinfo\"):\n            return\n        _glxinfo = glxinfo.GlxInfo()\n        if not hasattr(_glxinfo, \"display\"):\n            logger.warning(\"Invalid glxinfo received\")\n            return\n        return _glxinfo\n\n    def get_requirements(self, include_optional=True):\n        \"\"\"Return used system requirements\"\"\"\n        _requirements = self.required_components.copy()\n        if include_optional:\n            _requirements += self.optional_components\n            if drivers.is_amd():\n                _requirements.append(\"RADEON\")\n        return _requirements\n\n    def get(self, command):\n        \"\"\"Return a system command path if available\"\"\"\n        return self._cache[\"COMMANDS\"].get(command)\n\n    def get_terminals(self):\n        \"\"\"Return list of installed terminals\"\"\"\n        return list(self._cache[\"TERMINALS\"].values())\n\n    def get_soundfonts(self):\n        \"\"\"Return path of available soundfonts\"\"\"\n        return self._cache[\"SOUNDFONTS\"]\n\n    def iter_lib_folders(self):\n        \"\"\"Loop over existing 32/64 bit library folders\"\"\"\n        for lib_paths in self.lib_folders:\n            if self.arch != 'x86_64':\n                # On non amd64 setups, only the first element is relevant\n                lib_paths = [lib_paths[0]]\n            yield lib_paths\n\n    def populate_libraries(self):\n        \"\"\"Populates the LIBRARIES cache with what is found on the system\"\"\"\n        self._cache[\"LIBRARIES\"] = {}\n        for arch in self.runtime_architectures:\n            self._cache[\"LIBRARIES\"][arch] = defaultdict(list)\n        for lib_paths in self.iter_lib_folders():\n            for path in lib_paths:\n                for req in self.requirements:\n                    for lib in SYSTEM_COMPONENTS[\"LIBRARIES\"][req]:\n                        for index, arch in enumerate(self.runtime_architectures):\n                            if os.path.exists(os.path.join(path, lib)):\n                                self._cache[\"LIBRARIES\"][arch][req].append(lib)\n\n    def populate_sound_fonts(self):\n        \"\"\"Populates the soundfont cache\"\"\"\n        self._cache[\"SOUNDFONTS\"] = []\n        for folder in self.soundfont_folders:\n            if not os.path.exists(folder):\n                continue\n            for soundfont in os.listdir(folder):\n                self._cache[\"SOUNDFONTS\"].append(soundfont)\n\n    def get_missing_requirement_libs(self, req):\n        \"\"\"Return a list of sets of missing libraries for each supported architecture\"\"\"\n        required_libs = set(SYSTEM_COMPONENTS[\"LIBRARIES\"][req])\n        return [\n            list(required_libs - set(self._cache[\"LIBRARIES\"][arch][req]))\n            for arch in self.runtime_architectures\n        ]\n\n    def get_missing_libs(self):\n        \"\"\"Return a dictionary of missing libraries\"\"\"\n        return {\n            req: self.get_missing_requirement_libs(req)\n            for req in self.requirements\n        }\n\n    def is_feature_supported(self, feature):\n        \"\"\"Return whether the system has the necessary libs to support a feature\"\"\"\n        return not self.get_missing_requirement_libs(feature)[0]\n\n\nLINUX_SYSTEM = LinuxSystem()\n\n\ndef gather_system_info():\n    \"\"\"Get all system information in a single data structure\"\"\"\n    system_info = {}\n    if drivers.is_nvidia():\n        system_info[\"nvidia_driver\"] = drivers.get_nvidia_driver_info()\n        system_info[\"nvidia_gpus\"] = [\n            drivers.get_nvidia_gpu_info(gpu_id)\n            for gpu_id in drivers.get_nvidia_gpu_ids()\n        ]\n    system_info[\"gpus\"] = [drivers.get_gpu_info(gpu) for gpu in drivers.get_gpus()]\n    system_info[\"env\"] = dict(os.environ)\n    system_info[\"missing_libs\"] = LINUX_SYSTEM.get_missing_libs()\n    system_info[\"cpus\"] = LINUX_SYSTEM.get_cpus()\n    system_info[\"drives\"] = LINUX_SYSTEM.get_drives()\n    system_info[\"ram\"] = LINUX_SYSTEM.get_ram_info()\n    system_info[\"dist\"] = LINUX_SYSTEM.get_dist_info()\n    system_info[\"glxinfo\"] = glxinfo.GlxInfo().as_dict()\n    return system_info\n", "path": "lutris/util/linux.py"}]}