Better graphical start
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
{ lib, config, pkgs, ... }: {
|
{ lib, config, pkgs, ... }: {
|
||||||
|
imports = [ ./network-watchdog.nix ];
|
||||||
|
|
||||||
#environment.sessionVariables.SOPS_AGE_KEY_FILE = keyFilePath;
|
#environment.sessionVariables.SOPS_AGE_KEY_FILE = keyFilePath;
|
||||||
systemd.user.startServices = lib.mkIf pkgs.stdenv.isLinux "sd-switch";
|
systemd.user.startServices = lib.mkIf pkgs.stdenv.isLinux "sd-switch";
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{ lib, config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.services.network-watchdog;
|
||||||
|
watchdog = "${pkgs.custom.network-watchdog}/bin/network-watchdog";
|
||||||
|
units = map (name: "${name}.service") cfg.units;
|
||||||
|
in {
|
||||||
|
# User services that only make sense while online: GUI clients that hold a
|
||||||
|
# socket to a server (jellyfin-mpv-shim, nextcloud-client). Their upstream
|
||||||
|
# units have no network ordering (network-online.target is a system target,
|
||||||
|
# out of reach for user units), so they happily start while offline —
|
||||||
|
# silently dead — and nothing notices a connection dropped by a network
|
||||||
|
# blip. Gate each start on the network being up, restart on failure, and let
|
||||||
|
# a watchdog catch the "up but not connected" state that systemd can't see.
|
||||||
|
# See pkgs.custom.network-watchdog.
|
||||||
|
options.services.network-watchdog.units = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
example = [ "jellyfin-mpv-shim" ];
|
||||||
|
description = "User services (no .service suffix) to gate on the network and keep connected.";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (cfg.units != [ ]) {
|
||||||
|
systemd.user.services = lib.genAttrs cfg.units (name: {
|
||||||
|
Service = {
|
||||||
|
ExecStartPre = "${watchdog} wait-online ${name}.service";
|
||||||
|
TimeoutStartSec = "infinity"; # stay in start-pre until the network is back
|
||||||
|
Restart = lib.mkDefault "on-failure";
|
||||||
|
RestartSec = lib.mkDefault 5;
|
||||||
|
};
|
||||||
|
}) // {
|
||||||
|
network-watchdog = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Restart network services that are up but not connected";
|
||||||
|
After = units;
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${watchdog} check ${lib.concatStringsSep " " units}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.timers.network-watchdog = {
|
||||||
|
Unit.Description = "Periodic health check for network services";
|
||||||
|
Timer = {
|
||||||
|
OnActiveSec = "60s";
|
||||||
|
OnUnitActiveSec = "60s";
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "timers.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{ lib, config, pkgs, ... }:
|
{ lib, config, pkgs, ... }:
|
||||||
let
|
let
|
||||||
configuredMpv = config.programs.mpv.finalPackage;
|
configuredMpv = config.programs.mpv.finalPackage;
|
||||||
shimWatchdog = pkgs.custom.jellyfin-mpv-shim-watchdog;
|
|
||||||
in{
|
in{
|
||||||
|
|
||||||
config = lib.mkIf (config.syscfg.make.gui) {
|
config = lib.mkIf (config.syscfg.make.gui) {
|
||||||
@@ -83,36 +82,6 @@ in{
|
|||||||
|
|
||||||
programs.yt-dlp.enable = true;
|
programs.yt-dlp.enable = true;
|
||||||
|
|
||||||
# The upstream jellyfin-mpv-shim service has no Restart and no network
|
services.network-watchdog.units = [ "jellyfin-mpv-shim" ];
|
||||||
# ordering: it happily starts while offline (silently dead) and never
|
|
||||||
# recovers a connection dropped by a network blip. Gate its start on the
|
|
||||||
# network being up, and let a watchdog notice the "up but not connected"
|
|
||||||
# state that systemd can't see. See pkgs.custom.jellyfin-mpv-shim-watchdog.
|
|
||||||
systemd.user.services.jellyfin-mpv-shim.Service = {
|
|
||||||
ExecStartPre = "${shimWatchdog}/bin/jellyfin-mpv-shim-watchdog wait-online";
|
|
||||||
TimeoutStartSec = "infinity"; # stay in start-pre until the network is back
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.user.services.jellyfin-mpv-shim-watchdog = {
|
|
||||||
Unit = {
|
|
||||||
Description = "Restart jellyfin-mpv-shim when it is up but not connected";
|
|
||||||
After = [ "jellyfin-mpv-shim.service" ];
|
|
||||||
};
|
|
||||||
Service = {
|
|
||||||
Type = "oneshot";
|
|
||||||
ExecStart = "${shimWatchdog}/bin/jellyfin-mpv-shim-watchdog check";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.user.timers.jellyfin-mpv-shim-watchdog = {
|
|
||||||
Unit.Description = "Periodic health check for jellyfin-mpv-shim";
|
|
||||||
Timer = {
|
|
||||||
OnActiveSec = "60s";
|
|
||||||
OnUnitActiveSec = "60s";
|
|
||||||
};
|
|
||||||
Install.WantedBy = [ "timers.target" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{ lib, config, ... }: {
|
{ lib, config, ... }: {
|
||||||
|
|
||||||
config = lib.mkIf (config.syscfg.make.gui) {
|
config = lib.mkIf (config.syscfg.make.gui) {
|
||||||
|
services.easyeffects.enable = true;
|
||||||
|
|
||||||
xdg.configFile."pipewire/pipewire-pulse.conf.d/desktop.conf".text = ''
|
xdg.configFile."pipewire/pipewire-pulse.conf.d/desktop.conf".text = ''
|
||||||
context.modules = [
|
context.modules = [
|
||||||
{ name = libpipewire-module-loopback
|
{ name = libpipewire-module-loopback
|
||||||
|
|||||||
@@ -1,7 +1,25 @@
|
|||||||
{ lib, config, pkgs, ... }: {
|
{ lib, config, pkgs, ... }: {
|
||||||
|
|
||||||
config = lib.mkIf (config.syscfg.make.gui) {
|
config = lib.mkIf (config.syscfg.make.gui) {
|
||||||
services.nextcloud-client.enable = true;
|
services.nextcloud-client = {
|
||||||
|
enable = true;
|
||||||
|
startInBackground = true;
|
||||||
|
};
|
||||||
|
services.network-watchdog.units = [ "nextcloud-client" ];
|
||||||
|
|
||||||
|
systemd.user.services.keepassxc = lib.mkIf (config.usercfg.wm != "X11") {
|
||||||
|
Unit = {
|
||||||
|
Description = "KeePassXC";
|
||||||
|
After = [ "graphical-session.target" ];
|
||||||
|
PartOf = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
ExecStart = "${pkgs.keepassxc}/bin/keepassxc";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 5;
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
thunar
|
thunar
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ let
|
|||||||
in {
|
in {
|
||||||
|
|
||||||
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
||||||
|
systemd.user.services.kanshi.Unit.After = lib.mkForce [ config.services.kanshi.systemdTarget "awww.service" ];
|
||||||
|
|
||||||
services.kanshi = {
|
services.kanshi = {
|
||||||
enable = true;
|
enable = true;
|
||||||
systemdTarget = "graphical-session.target";
|
systemdTarget = "graphical-session.target";
|
||||||
|
|||||||
@@ -24,23 +24,15 @@
|
|||||||
|
|
||||||
|
|
||||||
startupScript = pkgs.writeShellScriptBin "hyprland-start" ''
|
startupScript = pkgs.writeShellScriptBin "hyprland-start" ''
|
||||||
# The eww bar is not started here: eww.service opens it with the
|
|
||||||
# graphical session and kanshi places it per profile (see apps/eww).
|
|
||||||
${pkgs.awww}/bin/awww-daemon &
|
|
||||||
${pkgs.awww}/bin/awww restore &
|
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
keepassxc &
|
|
||||||
firefox &
|
firefox &
|
||||||
easyeffects --gapplication-service &
|
|
||||||
|
|
||||||
sleep 2
|
|
||||||
nextcloud &
|
|
||||||
# telegram-desktop &
|
# telegram-desktop &
|
||||||
# discord &
|
# discord &
|
||||||
'';
|
'';
|
||||||
in {
|
in {
|
||||||
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
||||||
|
services.awww.enable = true;
|
||||||
|
|
||||||
wayland.windowManager.hyprland = {
|
wayland.windowManager.hyprland = {
|
||||||
enable = true;
|
enable = true;
|
||||||
xwayland.enable = true;
|
xwayland.enable = true;
|
||||||
|
|||||||
@@ -108,7 +108,6 @@
|
|||||||
bspc desktop -f 0
|
bspc desktop -f 0
|
||||||
|
|
||||||
telegram-desktop &
|
telegram-desktop &
|
||||||
nextcloud &
|
|
||||||
#flameshot &
|
#flameshot &
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
@@ -117,7 +116,6 @@
|
|||||||
|
|
||||||
ibus-daemon -drx
|
ibus-daemon -drx
|
||||||
|
|
||||||
easyeffects --gapplication-service &
|
|
||||||
bspc desktop -f 0
|
bspc desktop -f 0
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ in {
|
|||||||
# wireplumber.enable = true;
|
# wireplumber.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [ easyeffects alsa-utils ];
|
programs.dconf.enable = true;
|
||||||
|
environment.systemPackages = with pkgs; [ alsa-utils ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
eww-bar = pkgs.callPackage ./eww-bar { };
|
eww-bar = pkgs.callPackage ./eww-bar { };
|
||||||
|
|
||||||
jellyfin-mpv-shim-watchdog = pkgs.callPackage ./jellyfin-mpv-shim-watchdog { };
|
network-watchdog = pkgs.callPackage ./network-watchdog { };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
{ writeShellScriptBin
|
|
||||||
, coreutils
|
|
||||||
, findutils
|
|
||||||
, gnugrep
|
|
||||||
, iproute2
|
|
||||||
, systemd
|
|
||||||
}:
|
|
||||||
|
|
||||||
# jellyfin-mpv-shim keeps a persistent socket to the Jellyfin server. When the
|
|
||||||
# machine drops off the network that socket dies, but the process stays alive
|
|
||||||
# and the unit stays "active" — running yet deaf to cast requests, the "up but
|
|
||||||
# not working, restart it by hand" case. systemd can't see this: nothing
|
|
||||||
# crashes and the shim speaks no health protocol, so we probe the real thing —
|
|
||||||
# is its MainPID still holding a live (established, non-loopback) connection?
|
|
||||||
#
|
|
||||||
# check (default) one tick, run by a 60s timer: restart if online but
|
|
||||||
# not connected, rate-limited to one restart per 5 min
|
|
||||||
# wait-online block until a real address exists; used as ExecStartPre so the
|
|
||||||
# unit only starts once online
|
|
||||||
|
|
||||||
writeShellScriptBin "jellyfin-mpv-shim-watchdog" ''
|
|
||||||
set -u
|
|
||||||
service="jellyfin-mpv-shim.service"
|
|
||||||
stamp="''${XDG_RUNTIME_DIR:-/run/user/$(${coreutils}/bin/id -u)}/jellyfin-mpv-shim-watchdog.stamp"
|
|
||||||
|
|
||||||
# A global-scope address on a non-loopback interface — true for a LAN IP too,
|
|
||||||
# so this needs no server URL and works whether Jellyfin is local or remote.
|
|
||||||
network_up() { ${iproute2}/bin/ip -o addr show scope global up 2>/dev/null | ${gnugrep}/bin/grep -q .; }
|
|
||||||
|
|
||||||
if [ "''${1:-check}" = wait-online ]; then
|
|
||||||
while ! network_up; do ${coreutils}/bin/sleep 5; done
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Healthy: MainPID owns an established socket to something other than loopback.
|
|
||||||
pid="$(${systemd}/bin/systemctl --user show -p MainPID --value "$service" 2>/dev/null)"
|
|
||||||
if [ "''${pid:-0}" != 0 ] && ${iproute2}/bin/ss -tnpH state established 2>/dev/null \
|
|
||||||
| ${gnugrep}/bin/grep "pid=$pid," \
|
|
||||||
| ${gnugrep}/bin/grep -qv -e '127\.0\.0\.1' -e '\[::1\]'; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Not connected: only worth restarting once the network is actually back...
|
|
||||||
network_up || exit 0
|
|
||||||
# ...and not if we already restarted within the last 5 minutes.
|
|
||||||
[ -e "$stamp" ] && [ -n "$(${findutils}/bin/find "$stamp" -mmin -5 2>/dev/null)" ] && exit 0
|
|
||||||
|
|
||||||
: > "$stamp"
|
|
||||||
echo "jellyfin-mpv-shim-watchdog: online but no live connection -> restarting $service"
|
|
||||||
${systemd}/bin/systemctl --user restart "$service"
|
|
||||||
''
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{ writeShellScriptBin
|
||||||
|
, coreutils
|
||||||
|
, findutils
|
||||||
|
, gnugrep
|
||||||
|
, iproute2
|
||||||
|
, systemd
|
||||||
|
}:
|
||||||
|
|
||||||
|
# For user services that only make sense while online (jellyfin-mpv-shim,
|
||||||
|
# nextcloud-client, ...). They keep a persistent socket to their server; when
|
||||||
|
# the machine drops off the network that socket dies, but the process stays
|
||||||
|
# alive and the unit stays "active" — running yet deaf, the "up but not
|
||||||
|
# working, restart it by hand" case. systemd can't see this: nothing crashes
|
||||||
|
# and the programs speak no health protocol, so we probe the real thing — is
|
||||||
|
# the MainPID still holding a live (established, non-loopback) connection?
|
||||||
|
#
|
||||||
|
# wait-online UNIT block until a real address exists; used as ExecStartPre
|
||||||
|
# so UNIT only starts once online. Also stamps the start
|
||||||
|
# time so `check` leaves a freshly (re)started UNIT alone.
|
||||||
|
# check UNIT... one tick, run by a 60s timer: restart every UNIT that is
|
||||||
|
# active but not connected, at most once per 5 minutes.
|
||||||
|
#
|
||||||
|
# Wired up by the services.network-watchdog home-manager module.
|
||||||
|
|
||||||
|
writeShellScriptBin "network-watchdog" ''
|
||||||
|
set -u
|
||||||
|
stampdir="''${XDG_RUNTIME_DIR:-/run/user/$(${coreutils}/bin/id -u)}/network-watchdog"
|
||||||
|
${coreutils}/bin/mkdir -p "$stampdir"
|
||||||
|
|
||||||
|
usage() { echo "usage: network-watchdog wait-online UNIT | check UNIT..." >&2; exit 64; }
|
||||||
|
|
||||||
|
# A global-scope address on a non-loopback interface — true for a LAN IP too,
|
||||||
|
# so this needs no server URL and works whether the server is local or remote.
|
||||||
|
network_up() { ${iproute2}/bin/ip -o addr show scope global up 2>/dev/null | ${gnugrep}/bin/grep -q .; }
|
||||||
|
|
||||||
|
# Healthy: MainPID owns an established socket to something other than loopback.
|
||||||
|
connected() {
|
||||||
|
pid="$(${systemd}/bin/systemctl --user show -p MainPID --value "$1" 2>/dev/null)"
|
||||||
|
[ "''${pid:-0}" != 0 ] && ${iproute2}/bin/ss -tnpH state established 2>/dev/null \
|
||||||
|
| ${gnugrep}/bin/grep "pid=$pid," \
|
||||||
|
| ${gnugrep}/bin/grep -qv -e '127\.0\.0\.1' -e '\[::1\]'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stamped within the last 5 minutes: still connecting, or already restarted.
|
||||||
|
recently_started() { [ -n "$(${findutils}/bin/find "$stampdir/$1" -mmin -5 2>/dev/null)" ]; }
|
||||||
|
|
||||||
|
[ $# -ge 1 ] || usage
|
||||||
|
cmd="$1"; shift
|
||||||
|
case "$cmd" in
|
||||||
|
wait-online)
|
||||||
|
[ $# -eq 1 ] || usage
|
||||||
|
while ! network_up; do ${coreutils}/bin/sleep 5; done
|
||||||
|
: > "$stampdir/$1"
|
||||||
|
;;
|
||||||
|
check)
|
||||||
|
# Nothing can be connected while offline, and restarting wouldn't help.
|
||||||
|
network_up || exit 0
|
||||||
|
for unit; do
|
||||||
|
# Not "active" (inactive, failed, or still in wait-online): systemd's job, not ours.
|
||||||
|
${systemd}/bin/systemctl --user is-active --quiet "$unit" || continue
|
||||||
|
recently_started "$unit" && continue
|
||||||
|
connected "$unit" && continue
|
||||||
|
echo "network-watchdog: $unit is online but has no live connection -> restarting"
|
||||||
|
${systemd}/bin/systemctl --user restart "$unit" # its wait-online refreshes the stamp
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
*) usage ;;
|
||||||
|
esac
|
||||||
|
''
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
[ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
|
[ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
|
||||||
boot.initrd.kernelModules = [ ];
|
boot.initrd.kernelModules = [ ];
|
||||||
boot.kernelModules = [ "v4l2loopback" "kvm-amd" ];
|
boot.kernelModules = [ "v4l2loopback" "kvm-amd" ];
|
||||||
#boot.kernelPackages = pkgs.linuxPackages_latest;
|
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||||
boot.extraModulePackages = with config.boot.kernelPackages;
|
boot.extraModulePackages = with config.boot.kernelPackages;
|
||||||
[ v4l2loopback ];
|
[ v4l2loopback ];
|
||||||
boot.extraModprobeConfig = ''
|
boot.extraModprobeConfig = ''
|
||||||
|
|||||||
Reference in New Issue
Block a user