From b94a54ec98914ad38940b5908da0466820a3b257 Mon Sep 17 00:00:00 2001 From: Daniel Olsen Date: Mon, 16 Jan 2023 06:58:47 +0100 Subject: [PATCH] xsession: Start applications in their own scopes and limit their memory usage --- profiles/base/default.nix | 4 + profiles/xsession/default.nix | 22 +++--- scripts/dmenu_run_systemd | 139 ++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 9 deletions(-) create mode 100755 scripts/dmenu_run_systemd diff --git a/profiles/base/default.nix b/profiles/base/default.nix index 2390205..9c32a3b 100644 --- a/profiles/base/default.nix +++ b/profiles/base/default.nix @@ -23,6 +23,10 @@ in name = lib.mkOption { type = lib.types.str; }; + systemd = lib.mkOption { + type = lib.types.bool; + default = true; + }; eth = lib.mkOption { }; wlan = lib.mkOption { }; secondary-fs = lib.mkOption { diff --git a/profiles/xsession/default.nix b/profiles/xsession/default.nix index 3c6a933..f34a09a 100644 --- a/profiles/xsession/default.nix +++ b/profiles/xsession/default.nix @@ -4,6 +4,7 @@ let cfg = config.profiles.xsession; non-nixos = config.profiles.non-nixos; mkGL = program: "${lib.strings.optionalString non-nixos.enable "${pkgs.nixgl.auto.nixGLDefault}/bin/nixGL "}${program}"; + execScope = program: "exec bash -c \"systemd-run --user --scope --unit='app-i3-exec-$RANDOM' -p CollectMode=inactive-or-failed -p MemoryHigh=92% -p MemoryMax=98% \"${program}\"\""; in { imports = [ ./dunstrc.nix ./terminal.nix ./polybar.nix ]; @@ -49,6 +50,7 @@ in terminal = "${pkgs.kitty}/bin/kitty"; keybindings = let modifier = config.xsession.windowManager.i3.config.modifier; + dmenu = if config.machine.systemd then "${../../scripts/dmenu_run_systemd}" else "dmenu_run"; in lib.mkOptionDefault { "${modifier}+0" = "workspace 10"; "${modifier}+Shift+0" = "move container to workspace 10"; @@ -64,20 +66,22 @@ in "XF86MonBrightnessUp" = "exec --no-startup-id brightnessctl set +5%"; "XF86MonBrightnessDown" = "exec --no-startup-id brightnessctl set 5%-"; - "XF86Display" = "exec arandr"; - "Print" = "exec scrot %Y-%m-%d_$wx$h_scrot.png -z -e 'mv $f /home/daniel/Pictures/screenshots/'"; "${modifier}+Print" = "exec scrot /home/daniel/Pictures/Screenshots/%Y-%m-%d_$wx$h_scrot.png -z"; + + "XF86Display" = "exec arandr"; + "${modifier}+Shift+U" = "exec $HOME/.config/nixpkgs/nix-dotfiles/scripts/dmenuunicode"; - - "${modifier}+n" = "exec dolphin"; - "${modifier}+b" = "exec firefox"; - "${modifier}+t" = "exec gedit"; - "${modifier}+Shift+s" = "exec $HOME/.config/nixpkgs/nix-dotfiles/scripts/dmenuaudio"; - # "${modifier}+Return" = lib.mkForce "exec kitty"; - "${modifier}+Shift+Return" = "exec kitty -e ssh dandellion@lilith"; + "${modifier}+d" = "exec ${dmenu}"; + + "${modifier}+n" = execScope "dolphin"; + "${modifier}+b" = execScope "firefox"; + "${modifier}+t" = execScope "gedit"; + + "${modifier}+Return" = execScope "kitty"; + "${modifier}+Shift+Return" = execScope "kitty -e ssh dandellion@lilith"; }; startup = [ { diff --git a/scripts/dmenu_run_systemd b/scripts/dmenu_run_systemd new file mode 100755 index 0000000..d63cc62 --- /dev/null +++ b/scripts/dmenu_run_systemd @@ -0,0 +1,139 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT +# +# dmenu_run_systemd: start a program from dmenu as transient systemd .scope +# (C) Copyright Benjamin Block 2021 +# (C) Copyright Daniel Olsen 2023 +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# Requirements: +# - Package: bash +# - Package: dmenu +# - dmenu_path +# - dmenu +# - Package: coreutils +# - basenc +# - tr +# - Package: systemd +# - systemd-run +# - Package: util-linux +# - getopt +# +# Usage: dmenu_run_systemd +# +# Start demnu to select program to execute, then start selected program in +# background. +# +# Usage: dmenu_run_systemd [Options] [--] [[, [...]]] +# +# Start with without involving dmenu. +# E.g. in i3 config: `bindsym $mod+Return exec dmenu_run_systemd alacritty`. +# +# Options: +# -f, --forground Start as forground task (default: no) +# -p, --pwd Use the current ${PWD} as working directory (default: +# ${HOME}) + +declare -g prefix forground=false cpwd=false +declare -ga selection +if [ "${#}" -lt 1 ]; then + prefix="dmenu-" + selection=("$(dmenu_path | dmenu)") || exit 127 +else + prefix="xrun-" + + declare opts + opts="$(getopt --shell bash \ + -o "fp" \ + -l "forground,pwd" \ + -n "dmenu_run_systemd" -- "${@}")" || exit 122 + eval set -- "${opts}" + unset opts + + while true; do + case "${1}" in + '-f'|'--forground') + forground=true + ;; + '-p'|'--pwd') + cpwd=true + ;; + '--') shift; break;; + esac + shift + done + + selection=("${@}") +fi +readonly selection prefix + +declare -g name +# Max unit name length: 256 +# - ".scope" - 6 +# - "-" - 6 +# - "-" - 33 +# ------ +# "" <= 211 +read -r -d '' -n 192 name < <( + echo -n "${selection[*]}" | tr -c 'a-zA-Z0-9_-' '[_*]' || exit 1 + echo -e '\0' || exit 2 +) || exit 126 +readonly name +{ [ "${#name}" -gt 0 ] && [ "${#name}" -le 211 ]; } || exit 125 + +declare -g rand +# ~5 bits per character => 32*5 = ~160 bits random number +read -r -N 32 rand < <(basenc --base32 < /dev/urandom) || exit 124 +readonly rand +[ "${#rand}" -eq 32 ] || exit 123 + +declare -ga runargs=( + --quiet + --user # run in per-User slice + --scope # create transient `.scope` unit, + # instead of `.service` + --collect # garbage collect everything after run, + # even on failure + --slice="app.slice" # run as part of `app.slice` + --unit="${prefix}${name}-${rand}" + + -p MemoryHigh=92% + -p MemoryMax=98% + # unit name + --description="dmenu selection ${selection[*]@Q}" +) + +if ${cpwd}; then + runargs+=( --working-directory="${PWD:-/}" ) +else + runargs+=( --working-directory="${HOME:-/}" ) +fi + +readonly runargs + +## Debugging: +#declare -p prefix selection name rand runargs + +if ${forground}; then + systemd-run "${runargs[@]}" -- "${selection[@]}" +else + systemd-run "${runargs[@]}" -- "${selection[@]}" & +fi