mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-05-10 15:31:50 +02:00
Use a separate thread to read stderr when needed
This commit is contained in:
parent
d66af442b3
commit
bea256d6f1
3 changed files with 53 additions and 1 deletions
|
@ -5,6 +5,8 @@ import platform
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
|
from queue import Queue
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import IO, Callable, Iterator, Optional
|
from typing import IO, Callable, Iterator, Optional
|
||||||
|
|
||||||
|
@ -92,6 +94,7 @@ class IsolationProvider(ABC):
|
||||||
self.proc_stderr = subprocess.PIPE
|
self.proc_stderr = subprocess.PIPE
|
||||||
else:
|
else:
|
||||||
self.proc_stderr = subprocess.DEVNULL
|
self.proc_stderr = subprocess.DEVNULL
|
||||||
|
self.stderr_queue = Queue()
|
||||||
|
|
||||||
def should_capture_stderr(self) -> bool:
|
def should_capture_stderr(self) -> bool:
|
||||||
return self.debug or getattr(sys, "dangerzone_dev", False)
|
return self.debug or getattr(sys, "dangerzone_dev", False)
|
||||||
|
@ -363,3 +366,23 @@ class IsolationProvider(ABC):
|
||||||
f"{debug_log}" # no need for an extra newline here
|
f"{debug_log}" # no need for an extra newline here
|
||||||
f"{DOC_TO_PIXELS_LOG_END}"
|
f"{DOC_TO_PIXELS_LOG_END}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _stream_stderr(self, stderr, queue: Queue) -> None:
|
||||||
|
"""Read stderr in a separate thread to avoid blocking"""
|
||||||
|
try:
|
||||||
|
for line in stderr:
|
||||||
|
queue.put(line)
|
||||||
|
if self.debug:
|
||||||
|
log.debug(line.decode().strip())
|
||||||
|
except (ValueError, IOError) as e:
|
||||||
|
log.debug(f"Stderr stream closed: {e}")
|
||||||
|
|
||||||
|
def start_stderr_thread(self, process: subprocess.Popen) -> None:
|
||||||
|
"""Start a thread to read stderr from the process"""
|
||||||
|
if process.stderr:
|
||||||
|
stderr_thread = threading.Thread(
|
||||||
|
target=self._stream_stderr,
|
||||||
|
args=(process.stderr, self.stderr_queue),
|
||||||
|
daemon=True
|
||||||
|
)
|
||||||
|
stderr_thread.start()
|
||||||
|
|
|
@ -189,7 +189,22 @@ class Container(IsolationProvider):
|
||||||
+ command
|
+ command
|
||||||
)
|
)
|
||||||
args = [container_runtime] + args
|
args = [container_runtime] + args
|
||||||
return self.exec(args)
|
args_str = " ".join(shlex.quote(s) for s in args)
|
||||||
|
log.info("> " + args_str)
|
||||||
|
|
||||||
|
process = subprocess.Popen(
|
||||||
|
args,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, # Always pipe stderr
|
||||||
|
startupinfo=startupinfo,
|
||||||
|
start_new_session=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Start stderr reader thread
|
||||||
|
self.start_stderr_thread(process)
|
||||||
|
|
||||||
|
return process
|
||||||
|
|
||||||
def kill_container(self, name: str) -> None:
|
def kill_container(self, name: str) -> None:
|
||||||
"""Terminate a spawned container.
|
"""Terminate a spawned container.
|
||||||
|
|
|
@ -66,11 +66,25 @@ class DangerzoneCore(object):
|
||||||
) -> None:
|
) -> None:
|
||||||
def convert_doc(document: Document) -> None:
|
def convert_doc(document: Document) -> None:
|
||||||
try:
|
try:
|
||||||
|
# Clear any existing stderr output
|
||||||
|
while not self.isolation_provider.stderr_queue.empty():
|
||||||
|
self.isolation_provider.stderr_queue.get_nowait()
|
||||||
|
|
||||||
self.isolation_provider.convert(
|
self.isolation_provider.convert(
|
||||||
document,
|
document,
|
||||||
ocr_lang,
|
ocr_lang,
|
||||||
stdout_callback,
|
stdout_callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Process any stderr output that was captured
|
||||||
|
while not self.isolation_provider.stderr_queue.empty():
|
||||||
|
try:
|
||||||
|
line = self.isolation_provider.stderr_queue.get_nowait()
|
||||||
|
if stdout_callback:
|
||||||
|
stdout_callback(True, line.decode().strip(), -1)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Error processing stderr: {e}")
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
log.exception(
|
log.exception(
|
||||||
f"Unexpected error occurred while converting '{document}'"
|
f"Unexpected error occurred while converting '{document}'"
|
||||||
|
|
Loading…
Reference in a new issue