nohang: init at 0.2.0, nixos/nohang: init (#409254)

This commit is contained in:
Doron Behar
2025-12-29 12:25:44 +00:00
committed by GitHub
7 changed files with 233 additions and 0 deletions
+5
View File
@@ -6484,6 +6484,11 @@
}
];
};
Dev380 = {
name = "Dev380";
github = "Dev380";
githubId = 49997896;
};
developer-guy = {
name = "Batuhan Apaydın";
email = "developerguyn@gmail.com";
@@ -22,6 +22,8 @@
- [LibreChat](https://www.librechat.ai/), open-source self-hostable ChatGPT clone with Agents and RAG APIs. Available as [services.librechat](#opt-services.librechat.enable).
- [nohang](https://github.com/hakavlad/nohang), a daemon for Linux that prevents out of memory (OOM) situations from affecting system responsiveness. Available as [services.nohang](#opt-services.nohang.enable)
- [DankMaterialShell](https://danklinux.com), a complete desktop shell for Wayland compositors built with Quickshell. Available as [programs.dms-shell](#opt-programs.dms-shell.enable).
- [dms-greeter](https://danklinux.com), a modern display manager greeter for DankMaterialShell that works with greetd and supports multiple Wayland compositors. Available as [services.displayManager.dms-greeter](#opt-services.displayManager.dms-greeter.enable).
+1
View File
@@ -1532,6 +1532,7 @@
./services/system/localtimed.nix
./services/system/nix-daemon-firewall.nix
./services/system/nix-daemon.nix
./services/system/nohang.nix
./services/system/nscd.nix
./services/system/nvme-rs.nix
./services/system/saslauthd.nix
+113
View File
@@ -0,0 +1,113 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.nohang;
inherit (lib)
literalExpression
mkEnableOption
mkIf
mkOption
mkPackageOption
types
;
in
{
meta = {
maintainers = with lib.maintainers; [ Dev380 ];
};
options.services.nohang = {
enable = mkEnableOption "nohang, a daemon that keeps system responsiveness when Linux is out of memory";
package = mkPackageOption pkgs "nohang" { };
configPath = mkOption {
type = types.either (types.enum [
"basic"
"desktop"
]) types.path;
default = "desktop";
example = literalExpression "./my-nohang-config.conf";
description = ''
Configuration file to use with nohang. The default and desktop example configurations in the nohang repository
can be used by setting this to "basic" or "desktop" (which is the default). Otherwise, you can set it to the path
of a custom configuration file.
'';
};
};
config = mkIf cfg.enable {
systemd.services.nohang = {
description = "Sophisticated low memory handler";
documentation = [
"man:nohang(8)"
"https://github.com/hakavlad/nohang"
];
after = [ "sysinit.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart =
"${lib.getExe cfg.package} --monitor --config "
+ (
if cfg.configPath == "basic" then
"${cfg.package}/etc/nohang/nohang.conf"
else if cfg.configPath == "desktop" then
"${cfg.package}/etc/nohang/nohang-desktop.conf"
else
cfg.configPath
);
Slice = "hostcritical.slice";
SyslogIdentifier =
if cfg.configPath == "basic" then
"nohang"
else if cfg.configPath == "desktop" then
"nohang-desktop"
else
"nohang-custom-config";
KillMode = "mixed";
Restart = "always";
RestartSec = 0;
CPUSchedulingResetOnFork = true;
RestrictRealtime = "yes";
TasksMax = 25;
MemoryMax = "100M";
MemorySwapMax = "100M";
UMask = 27;
ProtectSystem = "strict";
ReadWritePaths = "/var/log";
InaccessiblePaths = "/home /root";
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
ProtectHostname = true;
MemoryDenyWriteExecute = "yes";
RestrictNamespaces = "yes";
LockPersonality = "yes";
PrivateTmp = true;
DeviceAllow = "/dev/kmsg rw";
DevicePolicy = "closed";
CapabilityBoundingSet = [
"CAP_KILL"
"CAP_IPC_LOCK"
"CAP_SYS_PTRACE"
"CAP_DAC_READ_SEARCH"
"CAP_DAC_OVERRIDE"
"CAP_AUDIT_WRITE"
"CAP_SETUID"
"CAP_SETGID"
"CAP_SYS_RESOURCE"
"CAP_SYSLOG"
];
};
};
};
}
+1
View File
@@ -1105,6 +1105,7 @@ in
nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; };
nixseparatedebuginfod2 = runTest ./nixseparatedebuginfod2.nix;
node-red = runTest ./node-red.nix;
nohang = runTest ./nohang.nix;
nomad = runTest ./nomad.nix;
nominatim = runTest ./nominatim.nix;
non-default-filesystems = handleTest ./non-default-filesystems.nix { };
+56
View File
@@ -0,0 +1,56 @@
# The following was modified from ./earlyoom.nix
{ lib, pkgs, ... }:
{
name = "nohang";
meta = {
maintainers = with lib.maintainers; [
Dev380
];
};
nodes.machine =
{ pkgs, ... }:
{
# Limit VM resource usage.
virtualisation.memorySize = 1024;
services.nohang.enable = true;
# disable other oom killers just in case
systemd.oomd.enable = false;
systemd.services.testbloat = {
description = "Create a lot of memory pressure";
serviceConfig = {
ExecStart = "${lib.getExe' pkgs.coreutils "tail"} /dev/zero";
};
};
};
# SIGTERM may be given so tail /dev/zero may or may not succeed
# The output will have have something like "Sending SIGTERM to /nix/store/87fc"
# with the truncated path so we'll check for that in the test
testScript = ''
machine.wait_for_unit("nohang.service")
with subtest("nohang should kill the bad service"):
machine.execute("systemctl start --wait testbloat.service")
signal_type = None
match machine.get_unit_info("testbloat.service")["Result"]:
case "signal":
signal_type = "SIGKILL"
case "success":
signal_type = "SIGTERM"
output = machine.succeed('journalctl -u nohang.service -b0')
if not f'[ OK ] Sending {signal_type}' in output:
raise Exception(f"'[ OK ] Sending {signal_type}' not in output")
if not 'The victim' in output:
raise Exception("'The victim' not in output")
if not 'Memory status after implementing a corrective action:' in output:
raise Exception("'Memory status after implementing a corrective action:' not in output")
if not 'FINISHING implement_corrective_action()' in output:
raise Exception("'FINISHING implement_corrective_action()' not in output")
if not f"{signal_type} to {'${pkgs.coreutils}'[:len('/nix/store/1234')]}: 1" in output:
raise Exception(f"'{signal_type} to {'${pkgs.coreutils}'[:len('/nix/store/1234')]}: 1' not in output")
'';
}
+55
View File
@@ -0,0 +1,55 @@
{
lib,
stdenv,
nixosTests,
fetchFromGitHub,
python3,
sudo,
libnotify,
coreutils,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "nohang";
version = "0.2.0";
src = fetchFromGitHub {
owner = "hakavlad";
repo = "nohang";
tag = "v${finalAttrs.version}";
hash = "sha256-aRiBGnJHdtQ5euvJ4DSuCZO5i5OJVRiCesaatrIARmg=";
};
postPatch = ''
patchShebangs src
substituteInPlace src/nohang \
--replace-fail 'notify-send' '${lib.getExe libnotify}' \
--replace-fail 'sudo' '${lib.getExe sudo}' \
--replace-fail "'env'" "'${lib.getExe' coreutils "env"}'"
'';
buildInputs = [ python3 ];
dontBuild = true;
installTargets = [ "base" ];
installFlags = [
"DESTDIR=${placeholder "out"}"
"PREFIX=/"
"SBINDIR=/sbin"
"SYSCONFDIR=/etc"
"SYSTEMDUNITDIR=/lib/systemd/system"
];
passthru.tests = {
inherit (nixosTests) nohang;
};
meta = {
homepage = "https://github.com/hakavlad/nohang";
description = "Sophisticated low memory handler for Linux";
license = with lib.licenses; [ mit ];
mainProgram = "nohang";
platforms = lib.platforms.linux;
maintainers = with lib.maintainers; [ Dev380 ];
};
})