diff --git a/src/extension/src/ProcessHandler.py b/src/extension/src/ProcessHandler.py index b3ea00ca..71030572 100644 --- a/src/extension/src/ProcessHandler.py +++ b/src/extension/src/ProcessHandler.py @@ -199,9 +199,27 @@ def is_process_running(self, pid): # According to "man 2 kill" possible error values are (EINVAL, EPERM, ESRCH) Thus considering this as an error return False + def is_process_patching_operation(self, pid): + try: + # Patching operation cmdline will look like: + # /usr/bin/python3.10 /var/lib/waagent/Microsoft.CPlat.Core.LinuxPatchExtension-1.6.69/MsftLinuxPatchCore.py + with self.env_layer.file_system.open("/proc/{0}/cmdline".format(str(pid)), mode="rb") as cmdline_file: + cmdline = cmdline_file.read() + cmdline = cmdline.replace(b'\x00', b' ').decode("utf-8") # cmdline is null-separated, so replacing nulls with spaces for easier parsing and logging + + if Constants.CORE_CODE_FILE_NAME in cmdline: + self.logger.log_verbose("Process is a patching operation. [PID={0}]".format(str(pid))) + return True + else: + self.logger.log_debug("Process is not a patching operation. [PID={0}] [CmdLine={1}]".format(str(pid), cmdline)) + return False + except Exception as error: + self.logger.log_debug("Error checking if process is a patching operation. [PID={0}] [Error={1}]".format(str(pid), repr(error))) + return True # If we cannot determine, assume it is a patching operation to be safe + def kill_process(self, pid): try: - if self.is_process_running(pid): + if self.is_process_running(pid) and self.is_process_patching_operation(pid): self.logger.log("Terminating process: [PID={0}]".format(str(pid))) os.kill(pid, signal.SIGTERM) except OSError as error: diff --git a/src/extension/tests/Test_ProcessHandler.py b/src/extension/tests/Test_ProcessHandler.py index 99e19250..b13ad870 100644 --- a/src/extension/tests/Test_ProcessHandler.py +++ b/src/extension/tests/Test_ProcessHandler.py @@ -49,6 +49,9 @@ def tearDown(self): def mock_is_process_running_to_return_true(self, pid): return True + def mock_is_process_patching_operation_to_return_true(self, pid): + return True + def mock_os_kill_to_raise_exception(self, pid, sig): raise OSError @@ -133,6 +136,8 @@ def test_kill_process(self): # setting mocks is_process_running_backup = ProcessHandler.is_process_running ProcessHandler.is_process_running = self.mock_is_process_running_to_return_true + is_process_patching_operation_backup = ProcessHandler.is_process_patching_operation + ProcessHandler.is_process_patching_operation = self.mock_is_process_patching_operation_to_return_true os_kill_backup = os.kill os.kill = self.mock_os_kill_to_raise_exception @@ -143,6 +148,7 @@ def test_kill_process(self): # reseting mocks ProcessHandler.is_process_running = is_process_running_backup + ProcessHandler.is_process_patching_operation = is_process_patching_operation_backup os.kill = os_kill_backup def test_get_python_cmd(self):