Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/native-linux-uinput/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,41 @@ The doctor checks Linux support, `/dev/uinput` candidates, write access, whether
the `uinput` module appears in `/proc/modules`, and prints explicit udev rule
templates. It does not change system permissions.

When a node exists but is not writable, the report includes its mode, owner UID,
and group GID. Compare those values with your user and groups (for example, run
`stat -c '%A %a %U:%G %n' /dev/uinput` and `id`). A node that remains unwritable
after it exists and its udev rules have settled points to persistent ownership
or mode restrictions. If access becomes available shortly after the module or
node starts, that points to a transient startup race. Retrying can help only
with timing; it cannot correct restrictive ownership or mode.

The setup output includes udev rules for access policy that should be applied
when the node is created. For an already-existing static `/dev/uinput` node,
`systemd-tmpfiles` is another option on systems that use it. Review your local
policy and the group before using this example:

```text
# /etc/tmpfiles.d/opencontroller-uinput.conf
z /dev/uinput 0660 root input -
```

Here `z` adjusts the mode and ownership of an existing path; it does not create
a missing device node. After reviewing and creating the file with an editor,
apply it explicitly:

```bash
sudo systemd-tmpfiles --create /etc/tmpfiles.d/opencontroller-uinput.conf
```

This grants write access to members of the `input` group, so use a group that
exists and whose membership is appropriate on your system. Membership may
grant broad access to input devices; it can require a new login session to take
effect. The tmpfiles rule handles the existing node when applied; a udev rule
is generally the better fit for access that should be applied whenever a node
is created. Neither option is installed or run by OpenController. See the
[systemd-tmpfiles documentation](https://www.freedesktop.org/software/systemd/man/latest/tmpfiles.d.html)
for the rule format and behavior.

## Run With OpenController JSONL

```bash
Expand Down
10 changes: 10 additions & 0 deletions packages/native-linux-uinput/src/__tests__/diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,17 @@ describe("linux uinput diagnostics", () => {
expect(diagnostics.ok).toBe(false);
expect(diagnostics.moduleLoaded).toBe(false);
expect(diagnostics.recommendations.join("\n")).toContain("modprobe");
expect(diagnostics.recommendations.join("\n")).toContain(
"persistent owner/mode denial",
);
expect(diagnostics.recommendations.join("\n")).toContain("transient race");
expect(diagnostics.recommendations.join("\n")).toContain(
"Retrying cannot fix restrictive permissions",
);
expect(diagnostics.recommendations.join("\n")).toContain("Grant write");
expect(formatLinuxUinputDiagnostics(diagnostics)).toContain(
"mode=0600 uid=0 gid=0",
);
});

test("provides explicit udev rule templates", () => {
Expand Down
5 changes: 4 additions & 1 deletion packages/native-linux-uinput/src/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,10 @@ function createRecommendations(
}
if (existingDevices.length > 0 && writableDevices.length === 0) {
recommendations.push(
"Grant write access to /dev/uinput using a reviewed udev rule, input group, or sudo for testing.",
"An existing uinput device node is not writable. Compare its reported mode/uid/gid with your user and groups. If access stays denied after the node and udev rules settle, this is a persistent owner/mode denial; access that appears shortly after module or node startup indicates a transient race. Retrying cannot fix restrictive permissions.",
);
recommendations.push(
"Grant write access using a reviewed udev rule or suitable group; use sudo only for a temporary test.",
);
}
if (existingDevices.some((device) => !device.characterDevice)) {
Expand Down
Loading