Skip to content

Commit

Permalink
nixos/logiops: init
Browse files Browse the repository at this point in the history
The logiops package already exists, but without a systemd service.

refactor: Use `udev` to restart service when mouse connected.

Old method used a somewhat jank `modprobe` to load the
`hid_logitech_hidpp` kernel module.

fix: Remove `IPAddressDeny` from service.

It is redundant since we already set `PrivateNetwork = true`.

fix: Allow service access to all of `/dev`.

If the user has many devices connected, they may run into issues with
the `DeviceAllow` permission. Ideally, we would only allow access to
`/dev/uinput` and `/dev/hidraw*`, but globbing is unavailable for device
node path specifications:

https://www.freedesktop.org/software/systemd/man/252/systemd.resource-control.html

refactor: Use `libconfig` generator.

fix: Format type should be `libconfig`.

fix: Incorrect way to add a file to `/etc`.

fix: DeviceAllow permissions.

`hidraw` is a character device in `/proc/devices`, so we should be able
to match it in `DeviceAllow`. Additionally, matching against `/dev`
doesn't do anything, as device node path specifications need to be fully
specified.

feat: Allow access to `AF_UNIX`.

This is needed since the newer version of `logiops` requires GDBus.

See PR#289701.

fix: Update service restart condition.

Sometimes the service will error out and fail to apply the user's
configuration if the mouse is disconnected and then reconnected. The
updated `udev` rule restarts the service any time a Logitech device is
connected.

feat: Add example config.

fix: systemd wantedBy option.

See: https://github.com/NixOS/nixpkgs/pull/287399/files/15700feac734cbacb3fb9dd0f5cbfc04a27058d9#r1493869013

feat: Allow package override.

fix: Use `literalExpression` for example package.

fix: Stop using `literalExpression` for example.

test: Add test to see if service starts properly.

fix: Revert `pkgs.` prefix for example.

fix: Only restart the service if it is already running.

refactor: Use overridden `systemd`.
  • Loading branch information
kylechui committed Mar 3, 2024
1 parent d8be97b commit 14d265d
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2405.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m

- [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable)

- [logiops](https://github.com/PixlOne/logiops), an unofficial userspace driver for HID++ Logitech devices. Available as [services.logiops](#opt-services.logiops.enable).

## Backward Incompatibilities {#sec-release-24.05-incompatibilities}

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@
./services/hardware/lirc.nix
./services/hardware/nvidia-container-toolkit-cdi-generator
./services/hardware/monado.nix
./services/hardware/logiops.nix
./services/hardware/nvidia-optimus.nix
./services/hardware/openrgb.nix
./services/hardware/pcscd.nix
Expand Down
113 changes: 113 additions & 0 deletions nixos/modules/services/hardware/logiops.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{ config, lib, pkgs, ... }:

let
inherit (lib) mkPackageOption mkEnableOption mkIf mkOption types;
cfg = config.services.logiops;
format = pkgs.formats.libconfig { };
in {
options.services.logiops = {
enable = mkEnableOption "logiops, an unofficial userspace driver for HID++ Logitech devices";
package = mkPackageOption pkgs "logiops" {
example = "logiops_0_2_3";
};

settings = mkOption {
type = types.submodule { freeformType = format.type; };
description = ''
Configuration for logiops.
See <https://github.com/PixlOne/logiops/wiki/Configuration> for more details.
Also see <https://github.com/PixlOne/logiops/blob/main/logid.example.cfg> for an example config.
'';
default = { };
example = {
devices = [{
name = "Wireless Mouse MX Master";
dpi = 1000;
buttons = [
{
cid = "0xc3";
action = {
type = "Gestures";
gestures = [
{
direction = "Up";
mode = "OnRelease";
action = {
type = "Keypress";
keys = [ "KEY_UP" ];
};
}
{
direction = "None";
mode = "NoPress";
}
];
};
}
{
cid = "0xc4";
action = {
type = "Keypress";
keys = [ "KEY_A" ];
};
}
];
}];
};
};
};

config = mkIf cfg.enable {
environment.etc."logid.cfg".source = format.generate "logid.cfg" cfg.settings;
systemd.services.logiops = {
description = "Logitech Configuration Daemon";
documentation = [ "https://github.com/PixlOne/logiops" ];

wantedBy = [ "multi-user.target" ];

startLimitIntervalSec = 0;
after = [ "multi-user.target" ];
wants = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/logid";
Restart = "always";
User = "root";

RuntimeDirectory = "logiops";

CapabilityBoundingSet = [ "CAP_SYS_NICE" ];
DeviceAllow = [ "/dev/uinput rw" "char-hidraw rw" ];
ProtectClock = true;
PrivateNetwork = true;
ProtectHome = true;
ProtectHostname = true;
PrivateUsers = true;
PrivateMounts = true;
PrivateTmp = true;
RestrictNamespaces = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
LockPersonality = true;
ProtectProc = "invisible";
SystemCallFilter = [ "nice" "@system-service" "~@privileged" ];
RestrictAddressFamilies = [ "AF_NETLINK" "AF_UNIX" ];
RestrictSUIDSGID = true;
NoNewPrivileges = true;
ProtectSystem = "strict";
ProcSubset = "pid";
UMask = "0077";
};
};

# Add a `udev` rule to restart `logiops` when the mouse is connected
# https://github.com/PixlOne/logiops/issues/239#issuecomment-1044122412
services.udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="input", ATTRS{id/vendor}=="046d", RUN{program}="${config.systemd.package}/bin/systemctl --no-block try-restart logiops.service"
'';
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ in {
lldap = handleTest ./lldap.nix {};
locate = handleTest ./locate.nix {};
login = handleTest ./login.nix {};
logiops = handleTest ./logiops.nix {};
logrotate = handleTest ./logrotate.nix {};
loki = handleTest ./loki.nix {};
luks = handleTest ./luks.nix {};
Expand Down
54 changes: 54 additions & 0 deletions nixos/tests/logiops.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import ./make-test-python.nix ({ pkgs, lib, ... }: {
name = "logiops";
meta = with pkgs.lib.maintainers; { maintainers = [ ckie ]; };

nodes = {
main = { ... }: {
services.logiops = {
enable = true;

settings = {
devices = [{
name = "Wireless Mouse MX Master 3";

smartshift = {
on = true;
threshold = 20;
};

hiresscroll = {
hires = true;
invert = false;
target = false;
};

dpi = 1500;

buttons = [
{
cid = "0x53";
action = {
type = "Keypress";
keys = [ "KEY_FORWARD" ];
};
}
{
cid = "0x56";
action = {
type = "Keypress";
keys = [ "KEY_BACK" ];
};
}
];
}];
};
};
};
};

testScript = ''
start_all()
with subtest("main machine should succeed"):
main.wait_for_unit("logiops.service")
'';
})

0 comments on commit 14d265d

Please sign in to comment.