diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 63e9f528..4e2315e8 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -84,6 +84,8 @@ class IOUVM(BaseNode): self._started = False self._nvram_watcher = None self._path = self.manager.get_abs_image_path(path, project.path) + self._lib_base = self.manager.get_images_directory() + self._loader = None self._license_check = True # IOU settings @@ -143,6 +145,7 @@ class IOUVM(BaseNode): """ self._path = self.manager.get_abs_image_path(path, self.project.path) + self._loader = None log.info(f'IOU "{self._name}" [{self._id}]: IOU image updated to "{self._path}"') @property @@ -174,9 +177,10 @@ class IOUVM(BaseNode): Finds the default RAM and NVRAM values for the IOU image. """ + await self._check_requirements() try: output = await gns3server.utils.asyncio.subprocess_check_output( - self._path, "-h", cwd=self.working_dir, stderr=True + *self._loader, self._path, "-h", cwd=self.working_dir, stderr=True ) match = re.search(r"-n \s+Size of nvram in Kb \(default ([0-9]+)KB\)", output) if match: @@ -191,11 +195,13 @@ class IOUVM(BaseNode): await self.update_default_iou_values() - def _check_requirements(self): + async def _check_requirements(self): """ Checks the IOU image. """ + if self._loader is not None: + return # image already checked if not self._path: raise IOUError("IOU image is not configured") if not os.path.isfile(self._path) or not os.path.exists(self._path): @@ -219,6 +225,28 @@ class IOUVM(BaseNode): if not os.access(self._path, os.X_OK): raise IOUError(f"IOU image '{self._path}' is not executable") + # set loader command + if elf_header_start[4] == b"\x01": + # 32-bit loader + loader = os.path.join(self._lib_base, "lib", "ld-linux.so.2") + lib_path = (os.path.join(self._lib_base, "lib"), + os.path.join(self._lib_base, "lib", "i386-linux-gnu")) + else: + # 64-bit loader + loader = os.path.join(self._lib_base, "lib64", "ld-linux-x86-64.so.2") + lib_path = (os.path.join(self._lib_base, "lib64"), + os.path.join(self._lib_base, "lib", "x86_64-linux-gnu")) + self._loader = [] + if os.path.isfile(loader): + try: + proc = await asyncio.create_subprocess_exec(loader, "--verify", self._path) + if await proc.wait() == 0: + self._loader = [loader, "--library-path", ":".join(lib_path)] + else: + log.warning(f"Loader {loader} incompatible with '{self._path}'") + except (OSError, subprocess.SubprocessError) as e: + log.warning(f"Could not use loader {loader}: {e}") + def asdict(self): iou_vm_info = { @@ -385,8 +413,10 @@ class IOUVM(BaseNode): Checks for missing shared library dependencies in the IOU image. """ + env = os.environ.copy() + env["LD_TRACE_LOADED_OBJECTS"] = "1" try: - output = await gns3server.utils.asyncio.subprocess_check_output("ldd", self._path) + output = await gns3server.utils.asyncio.subprocess_check_output(*self._loader, self._path, env=env) except (OSError, subprocess.SubprocessError) as e: log.warning(f"Could not determine the shared library dependencies for {self._path}: {e}") return @@ -513,7 +543,7 @@ class IOUVM(BaseNode): Starts the IOU process. """ - self._check_requirements() + await self._check_requirements() if not self.is_running(): await self._library_check() @@ -560,10 +590,13 @@ class IOUVM(BaseNode): command = await self._build_command() try: - log.info(f"Starting IOU: {command}") + if self._loader: + log.info(f"Starting IOU: {command} with loader {self._loader}") + else: + log.info(f"Starting IOU: {command}") self.command_line = " ".join(command) self._iou_process = await asyncio.create_subprocess_exec( - *command, + *self._loader, *command, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE, stderr=subprocess.STDOUT, @@ -1125,7 +1158,7 @@ class IOUVM(BaseNode): env["IOURC"] = self.iourc_path try: output = await gns3server.utils.asyncio.subprocess_check_output( - self._path, "-h", cwd=self.working_dir, env=env, stderr=True + *self._loader, self._path, "-h", cwd=self.working_dir, env=env, stderr=True ) if re.search(r"-l\s+Enable Layer 1 keepalive messages", output): command.extend(["-l"]) diff --git a/tests/compute/iou/test_iou_vm.py b/tests/compute/iou/test_iou_vm.py index 8a1cf845..afd6fb78 100644 --- a/tests/compute/iou/test_iou_vm.py +++ b/tests/compute/iou/test_iou_vm.py @@ -48,6 +48,7 @@ async def vm(compute_project, manager, config, tmpdir, fake_iou_bin, iourc_file) vm = IOUVM("test", str(uuid.uuid4()), compute_project, manager, application_id=1) config.settings.IOU.iourc_path = iourc_file vm.path = "iou.bin" + vm._loader = [] return vm @@ -228,7 +229,8 @@ def test_path_relative(vm, fake_iou_bin): assert vm.path == fake_iou_bin -def test_path_invalid_bin(vm, tmpdir, config): +@pytest.mark.asyncio +async def test_path_invalid_bin(vm, tmpdir, config): config.settings.Server.images_path = str(tmpdir) path = str(tmpdir / "test.bin") @@ -238,7 +240,7 @@ def test_path_invalid_bin(vm, tmpdir, config): with pytest.raises(IOUError): vm.path = path - vm._check_requirements() + await vm._check_requirements() def test_create_netmap_config(vm):