Repository navigation
fix(office): stop all of LibreOffice at its time limit — kill the soffice process group - #302
Open
MrChengLen wants to merge 1 commit into
Open
MrChengLen wants to merge 1 commit into
MrChengLen wants to merge 1 commit into
Conversation
…fice process group soffice only launches LibreOffice: on Linux the soffice script execs the oosplash launcher, which runs the worker (soffice.bin) as its own child. Past its timeout, subprocess.run() stops only the program it started, so a DOCX -> PDF conversion that reached OFFICE_SUBPROCESS_TIMEOUT_SECONDS ended for the request, but not for LibreOffice's worker. _run_soffice() now starts soffice in a session of its own (start_new_session=True), so the call is one process group, and on a timeout or any other error while waiting it kills that group with SIGKILL before re-raising. It returns a CompletedProcess, so _convert_via_libreoffice and its callers (DocxToPdfConverter, Word files inserted by /pdf/insert) are unchanged. Where os.killpg does not exist (Windows) it falls back to proc.kill(), as before. Rejected: running soffice.bin directly (bypasses the launcher, which also handles LibreOffice's restart requests); wrapping the call in coreutils timeout (an extra dependency for what the standard library offers); killing the group after a normal exit as well (the group can be empty by then and its ID reused). Tests: tests/test_office_timeout.py (Linux) runs a stand-in launcher that starts a worker and waits for it. A control test shows that a plain subprocess.run timeout leaves the worker running; the fix test shows that _convert_via_libreoffice leaves nothing. docker-pr.yml gains a step that runs the real LibreOffice in the office image into its time limit on a long document and checks that its worker ran mid-conversion and is gone after the limit. The soffice fakes in test_office_gs_isolation.py and test_log_privacy.py now replace _run_soffice, and a new test there runs _run_soffice for real against a Python child on any platform: the env it is given reaches the child, and status and output come back the way subprocess.run(capture_output=True) returns them. Docs: self-hosting, architecture and the security overview say that the limit stops LibreOffice's worker along with the launcher; changelog fragment added. Full suite locally 3105 passed, 274 skipped (the 2 process tests skip on Windows and run in CI); the one local failure is the env-only test_stripe_ca_bundle (the local venv lags the lock; green in CI). ruff clean; gitleaks + scope guard clean; security and code review done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
DOCX → PDF through LibreOffice (office image) — and Word files inserted by
/pdf/insert— now stops all of LibreOffice whenOFFICE_SUBPROCESS_TIMEOUT_SECONDSis reached, not only the program the app started.Why
sofficeonly launches LibreOffice: on Linux thesofficescript execs theoosplashlauncher, which runs the worker (soffice.bin) as its own child. Past its timeout,subprocess.run()stops only the program it started, so the conversion ended for the request, but not for LibreOffice's worker.How
_run_soffice()inapp/converters/document.py:subprocess.Popen(..., start_new_session=True)makes the call one process group; on a timeout or any other error while waiting,os.killpg(..., SIGKILL)stops the whole group before the exception is re-raised. It returns aCompletedProcess, so_convert_via_libreofficeand its callers are unchanged (Windows, withoutos.killpg:proc.kill()as before).soffice.bindirectly (bypasses the launcher, which also handles LibreOffice's restart requests); wrapping the call in coreutilstimeout(extra dependency); killing the group after a normal exit too (the group can be empty by then and its ID reused).Tests
tests/test_office_timeout.py(Linux): a stand-in launcher that starts a worker and waits for it.subprocess.runtimeout leaves the worker running (proves the stand-in shows the case)._convert_via_libreofficewith a 1 s limit leaves nothing running..github/workflows/docker-pr.yml, office image: the real LibreOffice runs into a 5 s limit on a long document; the step checks that its worker ran mid-conversion and that none is left after the limit.test_office_gs_isolation.pyandtest_log_privacy.pynow replace_run_soffice; a new test there runs_run_sofficefor real against a Python child on any platform (the env it is given reaches the child; status and output come back likesubprocess.run(capture_output=True)).test_stripe_ca_bundle(local venv lags the lock; green in CI). ruff clean; gitleaks + scope guard clean; security and code review without blocking findings (their test and wording points are in). The two process tests skip on Windows and run here in CI.Docs
docs/self-hosting.md,docs/architecture.md,docs/security-overview.md: the limit stops LibreOffice's worker along with the launcher. Changelog fragmentchangelog.d/2026-10-29-libreoffice-timeout.md.🤖 Generated with Claude Code