diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 859d1e4b..50e132c1 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -88,6 +88,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 @@ -147,6 +149,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 @@ -178,9 +181,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: @@ -195,11 +199,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): @@ -223,6 +229,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] == 1: + # 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 = { @@ -391,8 +419,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 @@ -519,7 +549,7 @@ class IOUVM(BaseNode): Starts the IOU process. """ - self._check_requirements() + await self._check_requirements() if not self.is_running(): await self._library_check() @@ -566,10 +596,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, @@ -1131,7 +1164,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/gns3server/db/tasks.py b/gns3server/db/tasks.py index 48050eb7..d879fbb3 100644 --- a/gns3server/db/tasks.py +++ b/gns3server/db/tasks.py @@ -91,7 +91,8 @@ async def get_computes(app: FastAPI) -> List[dict]: def image_filter(change: Change, path: str) -> bool: if change == Change.added and os.path.isfile(path): - if path.endswith(".tmp") or path.endswith(".md5sum") or path.startswith("."): + if path.endswith(".tmp") or path.endswith(".md5sum") or path.startswith(".") or \ + "/lib/" in path or "/lib64/" in path: return False header_magic_len = 7 with open(path, "rb") as f: 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):