Skip to content

Commit c69a552

Browse files
authored
🐛 fix(unix): use correct runtime dir path for OpenBSD (#440)
OpenBSD applications using platformdirs were unnecessarily falling back to temporary directories because the default runtime path `/var/run/user/{uid}` is not writable by regular users on OpenBSD. 🔒 This issue was reported by OpenBSD package maintainers who noticed the mismatch with the platform's native XDG implementation. OpenBSD now has native `XDG_RUNTIME_DIR` support in libc via the `setxdgenv()` function in [`lib/libc/gen/login_cap.c`](https://github.com/openbsd/src/blob/master/lib/libc/gen/login_cap.c), which creates directories at `/tmp/run/user/{uid}` with proper ownership and permissions. The fix splits the BSD platform detection to handle OpenBSD separately from FreeBSD and NetBSD, which both continue using `/var/run/user/{uid}`. ✨ This change aligns platformdirs with OpenBSD's official implementation while preserving existing behavior for FreeBSD and NetBSD. Applications on OpenBSD will now correctly use the runtime directory created by the system during login instead of falling back to temporary directories. ## Documentation References During investigation, the following official platform documentation was reviewed: - **OpenBSD**: [`setxdgenv()` in login_cap.c](https://github.com/openbsd/src/blob/master/lib/libc/gen/login_cap.c) - uses `/tmp/run/user/{uid}` - **FreeBSD**: [`pam_xdg` module](https://github.com/freebsd/freebsd-src/blob/main/lib/libpam/modules/pam_xdg/pam_xdg.c) - uses `/var/run/xdg/{username}` (platformdirs fallback of `/var/run/user/{uid}` is reasonable) - **NetBSD**: No native XDG support found; [pkgsrc patches](https://github.com/NetBSD/pkgsrc/blob/trunk/misc/py-platformdirs/patches/patch-tests_test__unix.py) treat it same as FreeBSD Fixes #436
1 parent 03ccfd0 commit c69a552

2 files changed

Lines changed: 48 additions & 7 deletions

File tree

src/platformdirs/unix.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,13 @@ def user_runtime_dir(self) -> str:
150150
"""
151151
:return: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``.
152152
153-
If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/var/run/user/$(id -u)`` on
154-
FreeBSD/OpenBSD/NetBSD, ``/run/user/$(id -u)`` otherwise). If the default is not writable,
155-
falls back to a temporary directory.
153+
If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/tmp/run/user/$(id -u)`` on
154+
OpenBSD, ``/var/run/user/$(id -u)`` on FreeBSD/NetBSD, ``/run/user/$(id -u)`` otherwise).
155+
If the default is not writable, falls back to a temporary directory.
156156
"""
157-
if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
157+
if sys.platform.startswith("openbsd"):
158+
path = f"/tmp/run/user/{getuid()}" # noqa: S108
159+
elif sys.platform.startswith(("freebsd", "netbsd")):
158160
path = f"/var/run/user/{getuid()}"
159161
else:
160162
path = f"/run/user/{getuid()}"

tests/test_unix.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,18 +169,57 @@ def test_site_state_dir_fixed_path() -> None:
169169

170170

171171
@pytest.mark.usefixtures("_getuid")
172-
@pytest.mark.parametrize("platform", ["freebsd", "openbsd", "netbsd"])
173-
def test_platform_on_bsd(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, platform: str) -> None:
172+
@pytest.mark.parametrize("platform", [pytest.param("freebsd", id="freebsd"), pytest.param("netbsd", id="netbsd")])
173+
def test_freebsd_netbsd_site_runtime_dir(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, platform: str) -> None:
174174
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
175175
mocker.patch("sys.platform", platform)
176+
assert Unix().site_runtime_dir == "/var/run"
177+
178+
179+
@pytest.mark.usefixtures("_getuid")
180+
@pytest.mark.parametrize("platform", [pytest.param("freebsd", id="freebsd"), pytest.param("netbsd", id="netbsd")])
181+
def test_freebsd_netbsd_user_runtime_dir_writable(
182+
monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, platform: str
183+
) -> None:
184+
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
185+
mocker.patch("sys.platform", platform)
186+
mocker.patch("os.access", return_value=True)
187+
assert Unix().user_runtime_dir == "/var/run/user/1234"
188+
189+
190+
@pytest.mark.usefixtures("_getuid")
191+
@pytest.mark.parametrize("platform", [pytest.param("freebsd", id="freebsd"), pytest.param("netbsd", id="netbsd")])
192+
def test_freebsd_netbsd_user_runtime_dir_not_writable(
193+
monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, platform: str
194+
) -> None:
195+
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
196+
mocker.patch("sys.platform", platform)
197+
mocker.patch("os.access", return_value=False)
176198
mocker.patch("tempfile.tempdir", "/tmp") # noqa: S108
199+
assert Unix().user_runtime_dir == "/tmp/runtime-1234" # noqa: S108
200+
177201

202+
@pytest.mark.usefixtures("_getuid")
203+
def test_openbsd_site_runtime_dir(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture) -> None:
204+
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
205+
mocker.patch("sys.platform", "openbsd")
178206
assert Unix().site_runtime_dir == "/var/run"
179207

208+
209+
@pytest.mark.usefixtures("_getuid")
210+
def test_openbsd_user_runtime_dir_writable(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture) -> None:
211+
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
212+
mocker.patch("sys.platform", "openbsd")
180213
mocker.patch("os.access", return_value=True)
181-
assert Unix().user_runtime_dir == "/var/run/user/1234"
214+
assert Unix().user_runtime_dir == "/tmp/run/user/1234" # noqa: S108
215+
182216

217+
@pytest.mark.usefixtures("_getuid")
218+
def test_openbsd_user_runtime_dir_not_writable(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture) -> None:
219+
monkeypatch.delenv("XDG_RUNTIME_DIR", raising=False)
220+
mocker.patch("sys.platform", "openbsd")
183221
mocker.patch("os.access", return_value=False)
222+
mocker.patch("tempfile.tempdir", "/tmp") # noqa: S108
184223
assert Unix().user_runtime_dir == "/tmp/runtime-1234" # noqa: S108
185224

186225

0 commit comments

Comments
 (0)