diff options
Diffstat (limited to 'nixos/modules/programs')
121 files changed, 7857 insertions, 0 deletions
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix new file mode 100644 index 00000000000..83bcfe886aa --- /dev/null +++ b/nixos/modules/programs/adb.nix @@ -0,0 +1,30 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta.maintainers = [ maintainers.mic92 ]; + + ###### interface + options = { + programs.adb = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to configure system to use Android Debug Bridge (adb). + To grant access to a user, it must be part of adbusers group: + <code>users.users.alice.extraGroups = ["adbusers"];</code> + ''; + }; + }; + }; + + ###### implementation + config = mkIf config.programs.adb.enable { + services.udev.packages = [ pkgs.android-udev-rules ]; + # Give platform-tools lower priority so mke2fs+friends are taken from other packages first + environment.systemPackages = [ (lowPrio pkgs.androidenv.androidPkgs_9_0.platform-tools) ]; + users.groups.adbusers = {}; + }; +} diff --git a/nixos/modules/programs/appgate-sdp.nix b/nixos/modules/programs/appgate-sdp.nix new file mode 100644 index 00000000000..12cb542f4d0 --- /dev/null +++ b/nixos/modules/programs/appgate-sdp.nix @@ -0,0 +1,25 @@ +{ config, pkgs, lib, ... }: + +with lib; + +{ + options = { + programs.appgate-sdp = { + enable = mkEnableOption "AppGate SDP VPN client"; + }; + }; + + config = mkIf config.programs.appgate-sdp.enable { + boot.kernelModules = [ "tun" ]; + environment.systemPackages = [ pkgs.appgate-sdp ]; + services.dbus.packages = [ pkgs.appgate-sdp ]; + systemd = { + packages = [ pkgs.appgate-sdp ]; + # https://github.com/NixOS/nixpkgs/issues/81138 + services.appgatedriver.wantedBy = [ "multi-user.target" ]; + services.appgate-dumb-resolver.path = [ pkgs.e2fsprogs ]; + services.appgate-resolver.path = [ pkgs.procps pkgs.e2fsprogs ]; + services.appgatedriver.path = [ pkgs.e2fsprogs ]; + }; + }; +} diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix new file mode 100644 index 00000000000..ad75ab27666 --- /dev/null +++ b/nixos/modules/programs/atop.nix @@ -0,0 +1,155 @@ +# Global configuration for atop. + +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.programs.atop; + +in +{ + ###### interface + + options = { + + programs.atop = rec { + + enable = mkEnableOption "Atop"; + + package = mkOption { + type = types.package; + default = pkgs.atop; + defaultText = literalExpression "pkgs.atop"; + description = '' + Which package to use for Atop. + ''; + }; + + netatop = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to install and enable the netatop kernel module. + Note: this sets the kernel taint flag "O" for loading out-of-tree modules. + ''; + }; + package = mkOption { + type = types.package; + default = config.boot.kernelPackages.netatop; + defaultText = literalExpression "config.boot.kernelPackages.netatop"; + description = '' + Which package to use for netatop. + ''; + }; + }; + + atopgpu.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to install and enable the atopgpud daemon to get information about + NVIDIA gpus. + ''; + }; + + setuidWrapper.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to install a setuid wrapper for Atop. This is required to use some of + the features as non-root user (e.g.: ipc information, netatop, atopgpu). + Atop tries to drop the root privileges shortly after starting. + ''; + }; + + atopService.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the atop service responsible for storing statistics for + long-term analysis. + ''; + }; + atopRotateTimer.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the atop-rotate timer, which restarts the atop service + daily to make sure the data files are rotate. + ''; + }; + atopacctService.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the atopacct service which manages process accounting. + This allows Atop to gather data about processes that disappeared in between + two refresh intervals. + ''; + }; + settings = mkOption { + type = types.attrs; + default = { }; + example = { + flags = "a1f"; + interval = 5; + }; + description = '' + Parameters to be written to <filename>/etc/atoprc</filename>. + ''; + }; + }; + }; + + config = mkIf cfg.enable ( + let + atop = + if cfg.atopgpu.enable then + (cfg.package.override { withAtopgpu = true; }) + else + cfg.package; + in + { + environment.etc = mkIf (cfg.settings != { }) { + atoprc.text = concatStrings + (mapAttrsToList + (n: v: '' + ${n} ${toString v} + '') + cfg.settings); + }; + environment.systemPackages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ]; + boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ]; + systemd = + let + mkSystemd = type: cond: name: restartTriggers: { + ${name} = lib.mkIf cond { + inherit restartTriggers; + wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ]; + }; + }; + mkService = mkSystemd "services"; + mkTimer = mkSystemd "timers"; + in + { + packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ]; + services = + mkService cfg.atopService.enable "atop" [ atop ] + // mkService cfg.atopacctService.enable "atopacct" [ atop ] + // mkService cfg.netatop.enable "netatop" [ cfg.netatop.package ] + // mkService cfg.atopgpu.enable "atopgpu" [ atop ]; + timers = mkTimer cfg.atopRotateTimer.enable "atop-rotate" [ atop ]; + }; + + security.wrappers = lib.mkIf cfg.setuidWrapper.enable { + atop = + { setuid = true; + owner = "root"; + group = "root"; + source = "${atop}/bin/atop"; + }; + }; + } + ); +} diff --git a/nixos/modules/programs/autojump.nix b/nixos/modules/programs/autojump.nix new file mode 100644 index 00000000000..ecfc2f65807 --- /dev/null +++ b/nixos/modules/programs/autojump.nix @@ -0,0 +1,33 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.autojump; + prg = config.programs; +in +{ + options = { + programs.autojump = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable autojump. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + environment.pathsToLink = [ "/share/autojump" ]; + environment.systemPackages = [ pkgs.autojump ]; + + programs.bash.interactiveShellInit = "source ${pkgs.autojump}/share/autojump/autojump.bash"; + programs.zsh.interactiveShellInit = mkIf prg.zsh.enable "source ${pkgs.autojump}/share/autojump/autojump.zsh"; + programs.fish.interactiveShellInit = mkIf prg.fish.enable "source ${pkgs.autojump}/share/autojump/autojump.fish"; + }; +} diff --git a/nixos/modules/programs/bandwhich.nix b/nixos/modules/programs/bandwhich.nix new file mode 100644 index 00000000000..610d602ad2c --- /dev/null +++ b/nixos/modules/programs/bandwhich.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.programs.bandwhich; +in { + meta.maintainers = with maintainers; [ Br1ght0ne ]; + + options = { + programs.bandwhich = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add bandwhich to the global environment and configure a + setcap wrapper for it. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ bandwhich ]; + security.wrappers.bandwhich = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw,cap_net_admin+ep"; + source = "${pkgs.bandwhich}/bin/bandwhich"; + }; + }; +} diff --git a/nixos/modules/programs/bash-my-aws.nix b/nixos/modules/programs/bash-my-aws.nix new file mode 100644 index 00000000000..15e429a7549 --- /dev/null +++ b/nixos/modules/programs/bash-my-aws.nix @@ -0,0 +1,25 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + prg = config.programs; + cfg = prg.bash-my-aws; + + initScript = '' + eval $(${pkgs.bash-my-aws}/bin/bma-init) + ''; +in + { + options = { + programs.bash-my-aws = { + enable = mkEnableOption "bash-my-aws"; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ bash-my-aws ]; + + programs.bash.interactiveShellInit = initScript; + }; + } diff --git a/nixos/modules/programs/bash/bash-completion.nix b/nixos/modules/programs/bash/bash-completion.nix new file mode 100644 index 00000000000..b8e5b1bfa33 --- /dev/null +++ b/nixos/modules/programs/bash/bash-completion.nix @@ -0,0 +1,37 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + enable = config.programs.bash.enableCompletion; +in +{ + options = { + programs.bash.enableCompletion = mkEnableOption "Bash completion for all interactive bash shells" // { + default = true; + }; + }; + + config = mkIf enable { + programs.bash.promptPluginInit = '' + # Check whether we're running a version of Bash that has support for + # programmable completion. If we do, enable all modules installed in + # the system and user profile in obsolete /etc/bash_completion.d/ + # directories. Bash loads completions in all + # $XDG_DATA_DIRS/bash-completion/completions/ + # on demand, so they do not need to be sourced here. + if shopt -q progcomp &>/dev/null; then + . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" + nullglobStatus=$(shopt -p nullglob) + shopt -s nullglob + for p in $NIX_PROFILES; do + for m in "$p/etc/bash_completion.d/"*; do + . "$m" + done + done + eval "$nullglobStatus" + unset nullglobStatus p m + fi + ''; + }; +} diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix new file mode 100644 index 00000000000..7281126979e --- /dev/null +++ b/nixos/modules/programs/bash/bash.nix @@ -0,0 +1,217 @@ +# This module defines global configuration for the Bash shell, in +# particular /etc/bashrc and /etc/profile. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfge = config.environment; + + cfg = config.programs.bash; + + bashAliases = concatStringsSep "\n" ( + mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}") + (filterAttrs (k: v: v != null) cfg.shellAliases) + ); + +in + +{ + imports = [ + (mkRemovedOptionModule [ "programs" "bash" "enable" ] "") + ]; + + options = { + + programs.bash = { + + /* + enable = mkOption { + default = true; + description = '' + Whenever to configure Bash as an interactive shell. + Note that this tries to make Bash the default + <option>users.defaultUserShell</option>, + which in turn means that you might need to explicitly + set this variable if you have another shell configured + with NixOS. + ''; + type = types.bool; + }; + */ + + shellAliases = mkOption { + default = {}; + description = '' + Set of aliases for bash shell, which overrides <option>environment.shellAliases</option>. + See <option>environment.shellAliases</option> for an option format description. + ''; + type = with types; attrsOf (nullOr (either str path)); + }; + + shellInit = mkOption { + default = ""; + description = '' + Shell script code called during bash shell initialisation. + ''; + type = types.lines; + }; + + loginShellInit = mkOption { + default = ""; + description = '' + Shell script code called during login bash shell initialisation. + ''; + type = types.lines; + }; + + interactiveShellInit = mkOption { + default = ""; + description = '' + Shell script code called during interactive bash shell initialisation. + ''; + type = types.lines; + }; + + promptInit = mkOption { + default = '' + # Provide a nice prompt if the terminal supports it. + if [ "$TERM" != "dumb" ] || [ -n "$INSIDE_EMACS" ]; then + PROMPT_COLOR="1;31m" + ((UID)) && PROMPT_COLOR="1;32m" + if [ -n "$INSIDE_EMACS" ] || [ "$TERM" = "eterm" ] || [ "$TERM" = "eterm-color" ]; then + # Emacs term mode doesn't support xterm title escape sequence (\e]0;) + PS1="\n\[\033[$PROMPT_COLOR\][\u@\h:\w]\\$\[\033[0m\] " + else + PS1="\n\[\033[$PROMPT_COLOR\][\[\e]0;\u@\h: \w\a\]\u@\h:\w]\\$\[\033[0m\] " + fi + if test "$TERM" = "xterm"; then + PS1="\[\033]2;\h:\u:\w\007\]$PS1" + fi + fi + ''; + description = '' + Shell script code used to initialise the bash prompt. + ''; + type = types.lines; + }; + + promptPluginInit = mkOption { + default = ""; + description = '' + Shell script code used to initialise bash prompt plugins. + ''; + type = types.lines; + internal = true; + }; + + }; + + }; + + config = /* mkIf cfg.enable */ { + + programs.bash = { + + shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases; + + shellInit = '' + if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then + . ${config.system.build.setEnvironment} + fi + + ${cfge.shellInit} + ''; + + loginShellInit = cfge.loginShellInit; + + interactiveShellInit = '' + # Check the window size after every command. + shopt -s checkwinsize + + # Disable hashing (i.e. caching) of command lookups. + set +h + + ${cfg.promptInit} + ${cfg.promptPluginInit} + ${bashAliases} + + ${cfge.interactiveShellInit} + ''; + + }; + + environment.etc.profile.text = + '' + # /etc/profile: DO NOT EDIT -- this file has been generated automatically. + # This file is read for login shells. + + # Only execute this file once per shell. + if [ -n "$__ETC_PROFILE_SOURCED" ]; then return; fi + __ETC_PROFILE_SOURCED=1 + + # Prevent this file from being sourced by interactive non-login child shells. + export __ETC_PROFILE_DONE=1 + + ${cfg.shellInit} + ${cfg.loginShellInit} + + # Read system-wide modifications. + if test -f /etc/profile.local; then + . /etc/profile.local + fi + + if [ -n "''${BASH_VERSION:-}" ]; then + . /etc/bashrc + fi + ''; + + environment.etc.bashrc.text = + '' + # /etc/bashrc: DO NOT EDIT -- this file has been generated automatically. + + # Only execute this file once per shell. + if [ -n "$__ETC_BASHRC_SOURCED" ] || [ -n "$NOSYSBASHRC" ]; then return; fi + __ETC_BASHRC_SOURCED=1 + + # If the profile was not loaded in a parent process, source + # it. But otherwise don't do it because we don't want to + # clobber overridden values of $PATH, etc. + if [ -z "$__ETC_PROFILE_DONE" ]; then + . /etc/profile + fi + + # We are not always an interactive shell. + if [ -n "$PS1" ]; then + ${cfg.interactiveShellInit} + fi + + # Read system-wide modifications. + if test -f /etc/bashrc.local; then + . /etc/bashrc.local + fi + ''; + + # Configuration for readline in bash. We use "option default" + # priority to allow user override using both .text and .source. + environment.etc.inputrc.source = mkOptionDefault ./inputrc; + + users.defaultUserShell = mkDefault pkgs.bashInteractive; + + environment.pathsToLink = optionals cfg.enableCompletion [ + "/etc/bash_completion.d" + "/share/bash-completion" + ]; + + environment.shells = + [ "/run/current-system/sw/bin/bash" + "/run/current-system/sw/bin/sh" + "${pkgs.bashInteractive}/bin/bash" + "${pkgs.bashInteractive}/bin/sh" + ]; + + }; + +} diff --git a/nixos/modules/programs/bash/inputrc b/nixos/modules/programs/bash/inputrc new file mode 100644 index 00000000000..f339eb649ed --- /dev/null +++ b/nixos/modules/programs/bash/inputrc @@ -0,0 +1,37 @@ +# inputrc borrowed from CentOS (RHEL). + +set bell-style none + +set meta-flag on +set input-meta on +set convert-meta off +set output-meta on +set colored-stats on + +#set mark-symlinked-directories on + +$if mode=emacs + +# for linux console and RH/Debian xterm +"\e[1~": beginning-of-line +"\e[4~": end-of-line +"\e[5~": beginning-of-history +"\e[6~": end-of-history +"\e[3~": delete-char +"\e[2~": quoted-insert +"\e[5C": forward-word +"\e[5D": backward-word +"\e[1;5C": forward-word +"\e[1;5D": backward-word + +# for rxvt +"\e[8~": end-of-line + +# for non RH/Debian xterm, can't hurt for RH/DEbian xterm +"\eOH": beginning-of-line +"\eOF": end-of-line + +# for freebsd console +"\e[H": beginning-of-line +"\e[F": end-of-line +$endif diff --git a/nixos/modules/programs/bash/ls-colors.nix b/nixos/modules/programs/bash/ls-colors.nix new file mode 100644 index 00000000000..254ee14c477 --- /dev/null +++ b/nixos/modules/programs/bash/ls-colors.nix @@ -0,0 +1,20 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + enable = config.programs.bash.enableLsColors; +in +{ + options = { + programs.bash.enableLsColors = mkEnableOption "extra colors in directory listings" // { + default = true; + }; + }; + + config = mkIf enable { + programs.bash.promptPluginInit = '' + eval "$(${pkgs.coreutils}/bin/dircolors -b)" + ''; + }; +} diff --git a/nixos/modules/programs/bash/undistract-me.nix b/nixos/modules/programs/bash/undistract-me.nix new file mode 100644 index 00000000000..0e6465e048a --- /dev/null +++ b/nixos/modules/programs/bash/undistract-me.nix @@ -0,0 +1,36 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.bash.undistractMe; +in +{ + options = { + programs.bash.undistractMe = { + enable = mkEnableOption "notifications when long-running terminal commands complete"; + + playSound = mkEnableOption "notification sounds when long-running terminal commands complete"; + + timeout = mkOption { + default = 10; + description = '' + Number of seconds it would take for a command to be considered long-running. + ''; + type = types.int; + }; + }; + }; + + config = mkIf cfg.enable { + programs.bash.promptPluginInit = '' + export LONG_RUNNING_COMMAND_TIMEOUT=${toString cfg.timeout} + export UDM_PLAY_SOUND=${if cfg.playSound then "1" else "0"} + . "${pkgs.undistract-me}/etc/profile.d/undistract-me.sh" + ''; + }; + + meta = { + maintainers = with maintainers; [ kira-bruneau ]; + }; +} diff --git a/nixos/modules/programs/bcc.nix b/nixos/modules/programs/bcc.nix new file mode 100644 index 00000000000..e475c6ceaa6 --- /dev/null +++ b/nixos/modules/programs/bcc.nix @@ -0,0 +1,9 @@ +{ config, pkgs, lib, ... }: +{ + options.programs.bcc.enable = lib.mkEnableOption "bcc"; + + config = lib.mkIf config.programs.bcc.enable { + environment.systemPackages = [ pkgs.bcc ]; + boot.extraModulePackages = [ pkgs.bcc ]; + }; +} diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix new file mode 100644 index 00000000000..e1456d3c184 --- /dev/null +++ b/nixos/modules/programs/browserpass.nix @@ -0,0 +1,32 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + options.programs.browserpass.enable = mkEnableOption "Browserpass native messaging host"; + + config = mkIf config.programs.browserpass.enable { + environment.etc = let + appId = "com.github.browserpass.native.json"; + source = part: "${pkgs.browserpass}/lib/browserpass/${part}/${appId}"; + in { + # chromium + "chromium/native-messaging-hosts/${appId}".source = source "hosts/chromium"; + "chromium/policies/managed/${appId}".source = source "policies/chromium"; + + # chrome + "opt/chrome/native-messaging-hosts/${appId}".source = source "hosts/chromium"; + "opt/chrome/policies/managed/${appId}".source = source "policies/chromium"; + + # vivaldi + "opt/vivaldi/native-messaging-hosts/${appId}".source = source "hosts/chromium"; + "opt/vivaldi/policies/managed/${appId}".source = source "policies/chromium"; + + # brave + "opt/brave/native-messaging-hosts/${appId}".source = source "hosts/chromium"; + "opt/brave/policies/managed/${appId}".source = source "policies/chromium"; + }; + nixpkgs.config.firefox.enableBrowserpass = true; + }; +} diff --git a/nixos/modules/programs/calls.nix b/nixos/modules/programs/calls.nix new file mode 100644 index 00000000000..08a223b408d --- /dev/null +++ b/nixos/modules/programs/calls.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.calls; +in { + options = { + programs.calls = { + enable = mkEnableOption '' + Whether to enable GNOME calls: a phone dialer and call handler. + ''; + }; + }; + + config = mkIf cfg.enable { + programs.dconf.enable = true; + + environment.systemPackages = [ + pkgs.calls + ]; + + services.dbus.packages = [ + pkgs.callaudiod + ]; + }; +} diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix new file mode 100644 index 00000000000..aad554c2bd6 --- /dev/null +++ b/nixos/modules/programs/captive-browser.nix @@ -0,0 +1,152 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.captive-browser; + + inherit (lib) + concatStringsSep escapeShellArgs optionalString + literalExpression mkEnableOption mkIf mkOption mkOptionDefault types; + + browserDefault = chromium: concatStringsSep " " [ + ''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"'' + ''${chromium}/bin/chromium'' + ''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive'' + ''--proxy-server="socks5://$PROXY"'' + ''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"'' + ''--no-first-run'' + ''--new-window'' + ''--incognito'' + ''-no-default-browser-check'' + ''http://cache.nixos.org/'' + ]; + + desktopItem = pkgs.makeDesktopItem { + name = "captive-browser"; + desktopName = "Captive Portal Browser"; + exec = "/run/wrappers/bin/captive-browser"; + icon = "nix-snowflake"; + categories = [ "Network" ]; + }; + +in +{ + ###### interface + + options = { + programs.captive-browser = { + enable = mkEnableOption "captive browser"; + + package = mkOption { + type = types.package; + default = pkgs.captive-browser; + defaultText = literalExpression "pkgs.captive-browser"; + description = "Which package to use for captive-browser"; + }; + + interface = mkOption { + type = types.str; + description = "your public network interface (wlp3s0, wlan0, eth0, ...)"; + }; + + # the options below are the same as in "captive-browser.toml" + browser = mkOption { + type = types.str; + default = browserDefault pkgs.chromium; + defaultText = literalExpression (browserDefault "\${pkgs.chromium}"); + description = '' + The shell (/bin/sh) command executed once the proxy starts. + When browser exits, the proxy exits. An extra env var PROXY is available. + + Here, we use a separate Chrome instance in Incognito mode, so that + it can run (and be waited for) alongside the default one, and that + it maintains no state across runs. To configure this browser open a + normal window in it, settings will be preserved. + + @volth: chromium is to open a plain HTTP (not HTTPS nor redirect to HTTPS!) website. + upstream uses http://example.com but I have seen captive portals whose DNS server resolves "example.com" to 127.0.0.1 + ''; + }; + + dhcp-dns = mkOption { + type = types.str; + description = '' + The shell (/bin/sh) command executed to obtain the DHCP + DNS server address. The first match of an IPv4 regex is used. + IPv4 only, because let's be real, it's a captive portal. + ''; + }; + + socks5-addr = mkOption { + type = types.str; + default = "localhost:1666"; + description = "the listen address for the SOCKS5 proxy server"; + }; + + bindInterface = mkOption { + default = true; + type = types.bool; + description = '' + Binds <package>captive-browser</package> to the network interface declared in + <literal>cfg.interface</literal>. This can be used to avoid collisions + with private subnets. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + environment.systemPackages = [ + (pkgs.runCommandNoCC "captive-browser-desktop-item" { } '' + install -Dm444 -t $out/share/applications ${desktopItem}/share/applications/*.desktop + '') + ]; + + programs.captive-browser.dhcp-dns = + let + iface = prefixes: + optionalString cfg.bindInterface (escapeShellArgs (prefixes ++ [ cfg.interface ])); + in + mkOptionDefault ( + if config.networking.networkmanager.enable then + "${pkgs.networkmanager}/bin/nmcli dev show ${iface []} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS" + else if config.networking.dhcpcd.enable then + "${pkgs.dhcpcd}/bin/dhcpcd ${iface ["-U"]} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers" + else if config.networking.useNetworkd then + "${cfg.package}/bin/systemd-networkd-dns ${iface []}" + else + "${config.security.wrapperDir}/udhcpc --quit --now -f ${iface ["-i"]} -O dns --script ${ + pkgs.writeShellScript "udhcp-script" '' + if [ "$1" = bound ]; then + echo "$dns" + fi + ''}" + ); + + security.wrappers.udhcpc = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = "${pkgs.busybox}/bin/udhcpc"; + }; + + security.wrappers.captive-browser = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = pkgs.writeShellScript "captive-browser" '' + export PREV_CONFIG_HOME="$XDG_CONFIG_HOME" + export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" '' + browser = """${cfg.browser}""" + dhcp-dns = """${cfg.dhcp-dns}""" + socks5-addr = """${cfg.socks5-addr}""" + ${optionalString cfg.bindInterface '' + bind-device = """${cfg.interface}""" + ''} + ''} + exec ${cfg.package}/bin/captive-browser + ''; + }; + }; +} diff --git a/nixos/modules/programs/ccache.nix b/nixos/modules/programs/ccache.nix new file mode 100644 index 00000000000..0f7fd0a3683 --- /dev/null +++ b/nixos/modules/programs/ccache.nix @@ -0,0 +1,85 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.programs.ccache; +in { + options.programs.ccache = { + # host configuration + enable = mkEnableOption "CCache"; + cacheDir = mkOption { + type = types.path; + description = "CCache directory"; + default = "/var/cache/ccache"; + }; + # target configuration + packageNames = mkOption { + type = types.listOf types.str; + description = "Nix top-level packages to be compiled using CCache"; + default = []; + example = [ "wxGTK30" "ffmpeg" "libav_all" ]; + }; + }; + + config = mkMerge [ + # host configuration + (mkIf cfg.enable { + systemd.tmpfiles.rules = [ "d ${cfg.cacheDir} 0770 root nixbld -" ]; + + # "nix-ccache --show-stats" and "nix-ccache --clear" + security.wrappers.nix-ccache = { + owner = "root"; + group = "nixbld"; + setuid = false; + setgid = true; + source = pkgs.writeScript "nix-ccache.pl" '' + #!${pkgs.perl}/bin/perl + + %ENV=( CCACHE_DIR => '${cfg.cacheDir}' ); + sub untaint { + my $v = shift; + return '-C' if $v eq '-C' || $v eq '--clear'; + return '-V' if $v eq '-V' || $v eq '--version'; + return '-s' if $v eq '-s' || $v eq '--show-stats'; + return '-z' if $v eq '-z' || $v eq '--zero-stats'; + exec('${pkgs.ccache}/bin/ccache', '-h'); + } + exec('${pkgs.ccache}/bin/ccache', map { untaint $_ } @ARGV); + ''; + }; + }) + + # target configuration + (mkIf (cfg.packageNames != []) { + nixpkgs.overlays = [ + (self: super: genAttrs cfg.packageNames (pn: super.${pn}.override { stdenv = builtins.trace "with ccache: ${pn}" self.ccacheStdenv; })) + + (self: super: { + ccacheWrapper = super.ccacheWrapper.override { + extraConfig = '' + export CCACHE_COMPRESS=1 + export CCACHE_DIR="${cfg.cacheDir}" + export CCACHE_UMASK=007 + if [ ! -d "$CCACHE_DIR" ]; then + echo "=====" + echo "Directory '$CCACHE_DIR' does not exist" + echo "Please create it with:" + echo " sudo mkdir -m0770 '$CCACHE_DIR'" + echo " sudo chown root:nixbld '$CCACHE_DIR'" + echo "=====" + exit 1 + fi + if [ ! -w "$CCACHE_DIR" ]; then + echo "=====" + echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)" + echo "Please verify its access permissions" + echo "=====" + exit 1 + fi + ''; + }; + }) + ]; + }) + ]; +} diff --git a/nixos/modules/programs/cdemu.nix b/nixos/modules/programs/cdemu.nix new file mode 100644 index 00000000000..142e2934240 --- /dev/null +++ b/nixos/modules/programs/cdemu.nix @@ -0,0 +1,62 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.programs.cdemu; +in { + + options = { + programs.cdemu = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + <command>cdemu</command> for members of + <option>programs.cdemu.group</option>. + ''; + }; + group = mkOption { + type = types.str; + default = "cdrom"; + description = '' + Group that users must be in to use <command>cdemu</command>. + ''; + }; + gui = mkOption { + type = types.bool; + default = true; + description = '' + Whether to install the <command>cdemu</command> GUI (gCDEmu). + ''; + }; + image-analyzer = mkOption { + type = types.bool; + default = true; + description = '' + Whether to install the image analyzer. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + boot = { + extraModulePackages = [ config.boot.kernelPackages.vhba ]; + kernelModules = [ "vhba" ]; + }; + + services = { + udev.extraRules = '' + KERNEL=="vhba_ctl", MODE="0660", OWNER="root", GROUP="${cfg.group}" + ''; + dbus.packages = [ pkgs.cdemu-daemon ]; + }; + + environment.systemPackages = + [ pkgs.cdemu-daemon pkgs.cdemu-client ] + ++ optional cfg.gui pkgs.gcdemu + ++ optional cfg.image-analyzer pkgs.image-analyzer; + }; + +} diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix new file mode 100644 index 00000000000..8a1653318ab --- /dev/null +++ b/nixos/modules/programs/chromium.nix @@ -0,0 +1,112 @@ +{ config, lib, ... }: + +with lib; + +let + cfg = config.programs.chromium; + + defaultProfile = filterAttrs (k: v: v != null) { + HomepageLocation = cfg.homepageLocation; + DefaultSearchProviderEnabled = cfg.defaultSearchProviderEnabled; + DefaultSearchProviderSearchURL = cfg.defaultSearchProviderSearchURL; + DefaultSearchProviderSuggestURL = cfg.defaultSearchProviderSuggestURL; + ExtensionInstallForcelist = cfg.extensions; + }; +in + +{ + ###### interface + + options = { + programs.chromium = { + enable = mkEnableOption "<command>chromium</command> policies"; + + extensions = mkOption { + type = types.listOf types.str; + description = '' + List of chromium extensions to install. + For list of plugins ids see id in url of extensions on + <link xlink:href="https://chrome.google.com/webstore/category/extensions">chrome web store</link> + page. To install a chromium extension not included in the chrome web + store, append to the extension id a semicolon ";" followed by a URL + pointing to an Update Manifest XML file. See + <link xlink:href="https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExtensionInstallForcelist">ExtensionInstallForcelist</link> + for additional details. + ''; + default = []; + example = literalExpression '' + [ + "chlffgpmiacpedhhbkiomidkjlcfhogd" # pushbullet + "mbniclmhobmnbdlbpiphghaielnnpgdp" # lightshot + "gcbommkclmclpchllfjekcdonpmejbdp" # https everywhere + "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin + ] + ''; + }; + + homepageLocation = mkOption { + type = types.nullOr types.str; + description = "Chromium default homepage"; + default = null; + example = "https://nixos.org"; + }; + + defaultSearchProviderEnabled = mkOption { + type = types.nullOr types.bool; + description = "Enable the default search provider."; + default = null; + example = true; + }; + + defaultSearchProviderSearchURL = mkOption { + type = types.nullOr types.str; + description = "Chromium default search provider url."; + default = null; + example = + "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}"; + }; + + defaultSearchProviderSuggestURL = mkOption { + type = types.nullOr types.str; + description = "Chromium default search provider url for suggestions."; + default = null; + example = + "https://encrypted.google.com/complete/search?output=chrome&q={searchTerms}"; + }; + + extraOpts = mkOption { + type = types.attrs; + description = '' + Extra chromium policy options. A list of available policies + can be found in the Chrome Enterprise documentation: + <link xlink:href="https://cloud.google.com/docs/chrome-enterprise/policies/">https://cloud.google.com/docs/chrome-enterprise/policies/</link> + Make sure the selected policy is supported on Linux and your browser version. + ''; + default = {}; + example = literalExpression '' + { + "BrowserSignin" = 0; + "SyncDisabled" = true; + "PasswordManagerEnabled" = false; + "SpellcheckEnabled" = true; + "SpellcheckLanguage" = [ + "de" + "en-US" + ]; + } + ''; + }; + }; + }; + + ###### implementation + + config = lib.mkIf cfg.enable { + # for chromium + environment.etc."chromium/policies/managed/default.json".text = builtins.toJSON defaultProfile; + environment.etc."chromium/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts; + # for google-chrome https://www.chromium.org/administrators/linux-quick-start + environment.etc."opt/chrome/policies/managed/default.json".text = builtins.toJSON defaultProfile; + environment.etc."opt/chrome/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts; + }; +} diff --git a/nixos/modules/programs/clickshare.nix b/nixos/modules/programs/clickshare.nix new file mode 100644 index 00000000000..9980a7daf52 --- /dev/null +++ b/nixos/modules/programs/clickshare.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +{ + + options.programs.clickshare-csc1.enable = + lib.options.mkEnableOption '' + Barco ClickShare CSC-1 driver/client. + This allows users in the <literal>clickshare</literal> + group to access and use a ClickShare USB dongle + that is connected to the machine + ''; + + config = lib.modules.mkIf config.programs.clickshare-csc1.enable { + environment.systemPackages = [ pkgs.clickshare-csc1 ]; + services.udev.packages = [ pkgs.clickshare-csc1 ]; + users.groups.clickshare = {}; + }; + + meta.maintainers = [ lib.maintainers.yarny ]; + +} diff --git a/nixos/modules/programs/cnping.nix b/nixos/modules/programs/cnping.nix new file mode 100644 index 00000000000..d208d2b0704 --- /dev/null +++ b/nixos/modules/programs/cnping.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.cnping; +in +{ + options = { + programs.cnping = { + enable = mkEnableOption "Whether to install a setcap wrapper for cnping"; + }; + }; + + config = mkIf cfg.enable { + security.wrappers.cnping = { + source = "${pkgs.cnping}/bin/cnping"; + capabilities = "cap_net_raw+ep"; + }; + }; +} diff --git a/nixos/modules/programs/command-not-found/command-not-found.nix b/nixos/modules/programs/command-not-found/command-not-found.nix new file mode 100644 index 00000000000..4d2a89b5158 --- /dev/null +++ b/nixos/modules/programs/command-not-found/command-not-found.nix @@ -0,0 +1,95 @@ +# This module provides suggestions of packages to install if the user +# tries to run a missing command in Bash. This is implemented using a +# SQLite database that maps program names to Nix package names (e.g., +# "pdflatex" is mapped to "tetex"). + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.command-not-found; + commandNotFound = pkgs.substituteAll { + name = "command-not-found"; + dir = "bin"; + src = ./command-not-found.pl; + isExecutable = true; + inherit (cfg) dbPath; + perl = pkgs.perl.withPackages (p: [ p.DBDSQLite p.StringShellQuote ]); + }; + +in + +{ + options.programs.command-not-found = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether interactive shells should show which Nix package (if + any) provides a missing command. + ''; + }; + + dbPath = mkOption { + default = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite" ; + description = '' + Absolute path to programs.sqlite. + + By default this file will be provided by your channel + (nixexprs.tar.xz). + ''; + type = types.path; + }; + }; + + config = mkIf cfg.enable { + programs.bash.interactiveShellInit = + '' + # This function is called whenever a command is not found. + command_not_found_handle() { + local p='${commandNotFound}/bin/command-not-found' + if [ -x "$p" ] && [ -f '${cfg.dbPath}' ]; then + # Run the helper program. + "$p" "$@" + # Retry the command if we just installed it. + if [ $? = 126 ]; then + "$@" + else + return 127 + fi + else + echo "$1: command not found" >&2 + return 127 + fi + } + ''; + + programs.zsh.interactiveShellInit = + '' + # This function is called whenever a command is not found. + command_not_found_handler() { + local p='${commandNotFound}/bin/command-not-found' + if [ -x "$p" ] && [ -f '${cfg.dbPath}' ]; then + # Run the helper program. + "$p" "$@" + + # Retry the command if we just installed it. + if [ $? = 126 ]; then + "$@" + else + return 127 + fi + else + # Indicate than there was an error so ZSH falls back to its default handler + echo "$1: command not found" >&2 + return 127 + fi + } + ''; + + environment.systemPackages = [ commandNotFound ]; + }; + +} diff --git a/nixos/modules/programs/command-not-found/command-not-found.pl b/nixos/modules/programs/command-not-found/command-not-found.pl new file mode 100644 index 00000000000..72e246c81ae --- /dev/null +++ b/nixos/modules/programs/command-not-found/command-not-found.pl @@ -0,0 +1,77 @@ +#! @perl@/bin/perl -w + +use strict; +use DBI; +use DBD::SQLite; +use String::ShellQuote; +use Config; + +my $program = $ARGV[0]; + +my $dbPath = "@dbPath@"; + +my $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "") + or die "cannot open database `$dbPath'"; +$dbh->{RaiseError} = 0; +$dbh->{PrintError} = 0; + +my $system = $ENV{"NIX_SYSTEM"} // $Config{myarchname}; + +my $res = $dbh->selectall_arrayref( + "select package from Programs where system = ? and name = ?", + { Slice => {} }, $system, $program); + +my $len = !defined $res ? 0 : scalar @$res; + +if ($len == 0) { + print STDERR "$program: command not found\n"; +} elsif ($len == 1) { + my $package = @$res[0]->{package}; + if ($ENV{"NIX_AUTO_RUN"} // "") { + if ($ENV{"NIX_AUTO_RUN_INTERACTIVE"} // "") { + while (1) { + print STDERR "'$program' from package '$package' will be run, confirm? [yn]: "; + chomp(my $comfirm = <STDIN>); + if (lc $comfirm eq "n") { + exit 0; + } elsif (lc $comfirm eq "y") { + last; + } + } + } + exec("nix-shell", "-p", $package, "--run", shell_quote("exec", @ARGV)); + } else { + print STDERR <<EOF; +The program '$program' is not in your PATH. You can make it available in an +ephemeral shell by typing: + nix-shell -p $package +EOF + } +} else { + if ($ENV{"NIX_AUTO_RUN"} // "") { + print STDERR "Select a package that provides '$program':\n"; + for my $i (0 .. $len - 1) { + print STDERR " [", $i + 1, "]: @$res[$i]->{package}\n"; + } + my $choice = 0; + while (1) { # exec will break this loop + no warnings "numeric"; + print STDERR "Your choice [1-${len}]: "; + # 0 can be invalid user input like non-number string + # so we start from 1 + $choice = <STDIN> + 0; + if (1 <= $choice && $choice <= $len) { + exec("nix-shell", "-p", @$res[$choice - 1]->{package}, + "--run", shell_quote("exec", @ARGV)); + } + } + } else { + print STDERR <<EOF; +The program '$program' is not in your PATH. It is provided by several packages. +You can make it available in an ephemeral shell by typing one of the following: +EOF + print STDERR " nix-shell -p $_->{package}\n" foreach @$res; + } +} + +exit 127; diff --git a/nixos/modules/programs/criu.nix b/nixos/modules/programs/criu.nix new file mode 100644 index 00000000000..1714e1331a4 --- /dev/null +++ b/nixos/modules/programs/criu.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.programs.criu; +in { + + options = { + programs.criu = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Install <command>criu</command> along with necessary kernel options. + ''; + }; + }; + }; + config = mkIf cfg.enable { + system.requiredKernelConfig = with config.lib.kernelConfig; [ + (isYes "CHECKPOINT_RESTORE") + ]; + boot.kernel.features.criu = true; + environment.systemPackages = [ pkgs.criu ]; + }; + +} diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix new file mode 100644 index 00000000000..265c41cbbbc --- /dev/null +++ b/nixos/modules/programs/dconf.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.dconf; + cfgDir = pkgs.symlinkJoin { + name = "dconf-system-config"; + paths = map (x: "${x}/etc/dconf") cfg.packages; + postBuild = '' + mkdir -p $out/profile + mkdir -p $out/db + '' + ( + concatStringsSep "\n" ( + mapAttrsToList ( + name: path: '' + ln -s ${path} $out/profile/${name} + '' + ) cfg.profiles + ) + ) + '' + ${pkgs.dconf}/bin/dconf update $out/db + ''; + }; +in +{ + ###### interface + + options = { + programs.dconf = { + enable = mkEnableOption "dconf"; + + profiles = mkOption { + type = types.attrsOf types.path; + default = {}; + description = "Set of dconf profile files, installed at <filename>/etc/dconf/profiles/<replaceable>name</replaceable></filename>."; + internal = true; + }; + + packages = mkOption { + type = types.listOf types.package; + default = []; + description = "A list of packages which provide dconf profiles and databases in <filename>/etc/dconf</filename>."; + }; + }; + }; + + ###### implementation + + config = mkIf (cfg.profiles != {} || cfg.enable) { + environment.etc.dconf = mkIf (cfg.profiles != {} || cfg.packages != []) { + source = cfgDir; + }; + + services.dbus.packages = [ pkgs.dconf ]; + + systemd.packages = [ pkgs.dconf ]; + + # For dconf executable + environment.systemPackages = [ pkgs.dconf ]; + + # Needed for unwrapped applications + environment.sessionVariables.GIO_EXTRA_MODULES = mkIf cfg.enable [ "${pkgs.dconf.lib}/lib/gio/modules" ]; + }; + +} diff --git a/nixos/modules/programs/digitalbitbox/default.nix b/nixos/modules/programs/digitalbitbox/default.nix new file mode 100644 index 00000000000..cabdf260cda --- /dev/null +++ b/nixos/modules/programs/digitalbitbox/default.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.digitalbitbox; +in + +{ + options.programs.digitalbitbox = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Installs the Digital Bitbox application and enables the complementary hardware module. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.digitalbitbox; + defaultText = literalExpression "pkgs.digitalbitbox"; + description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults."; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + hardware.digitalbitbox = { + enable = true; + package = cfg.package; + }; + }; + + meta = { + doc = ./doc.xml; + maintainers = with lib.maintainers; [ vidbina ]; + }; +} diff --git a/nixos/modules/programs/digitalbitbox/doc.xml b/nixos/modules/programs/digitalbitbox/doc.xml new file mode 100644 index 00000000000..c63201628db --- /dev/null +++ b/nixos/modules/programs/digitalbitbox/doc.xml @@ -0,0 +1,74 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-programs-digitalbitbox"> + <title>Digital Bitbox</title> + <para> + Digital Bitbox is a hardware wallet and second-factor authenticator. + </para> + <para> + The <literal>digitalbitbox</literal> programs module may be installed by + setting <literal>programs.digitalbitbox</literal> to <literal>true</literal> + in a manner similar to +<programlisting> +<xref linkend="opt-programs.digitalbitbox.enable"/> = true; +</programlisting> + and bundles the <literal>digitalbitbox</literal> package (see + <xref + linkend="sec-digitalbitbox-package" />), which contains the + <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries, along + with the hardware module (see + <xref + linkend="sec-digitalbitbox-hardware-module" />) which sets up the + necessary udev rules to access the device. + </para> + <para> + Enabling the digitalbitbox module is pretty much the easiest way to get a + Digital Bitbox device working on your system. + </para> + <para> + For more information, see + <link xlink:href="https://digitalbitbox.com/start_linux" />. + </para> + <section xml:id="sec-digitalbitbox-package"> + <title>Package</title> + + <para> + The binaries, <literal>dbb-app</literal> (a GUI tool) and + <literal>dbb-cli</literal> (a CLI tool), are available through the + <literal>digitalbitbox</literal> package which could be installed as + follows: +<programlisting> +<xref linkend="opt-environment.systemPackages"/> = [ + pkgs.digitalbitbox +]; +</programlisting> + </para> + </section> + <section xml:id="sec-digitalbitbox-hardware-module"> + <title>Hardware</title> + + <para> + The digitalbitbox hardware package enables the udev rules for Digital Bitbox + devices and may be installed as follows: +<programlisting> +<xref linkend="opt-hardware.digitalbitbox.enable"/> = true; +</programlisting> + </para> + + <para> + In order to alter the udev rules, one may provide different values for the + <literal>udevRule51</literal> and <literal>udevRule52</literal> attributes + by means of overriding as follows: +<programlisting> +programs.digitalbitbox = { + <link linkend="opt-programs.digitalbitbox.enable">enable</link> = true; + <link linkend="opt-programs.digitalbitbox.package">package</link> = pkgs.digitalbitbox.override { + udevRule51 = "something else"; + }; +}; +</programlisting> + </para> + </section> +</chapter> diff --git a/nixos/modules/programs/dmrconfig.nix b/nixos/modules/programs/dmrconfig.nix new file mode 100644 index 00000000000..d2a5117c48e --- /dev/null +++ b/nixos/modules/programs/dmrconfig.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.dmrconfig; + +in { + meta.maintainers = [ maintainers.etu ]; + + ###### interface + options = { + programs.dmrconfig = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to configure system to enable use of dmrconfig. This + enables the required udev rules and installs the program. + ''; + relatedPackages = [ "dmrconfig" ]; + }; + + package = mkOption { + default = pkgs.dmrconfig; + type = types.package; + defaultText = literalExpression "pkgs.dmrconfig"; + description = "dmrconfig derivation to use"; + }; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + services.udev.packages = [ cfg.package ]; + }; +} diff --git a/nixos/modules/programs/droidcam.nix b/nixos/modules/programs/droidcam.nix new file mode 100644 index 00000000000..9843a1f5be2 --- /dev/null +++ b/nixos/modules/programs/droidcam.nix @@ -0,0 +1,16 @@ +{ lib, pkgs, config, ... }: + +with lib; + +{ + options.programs.droidcam = { + enable = mkEnableOption "DroidCam client"; + }; + + config = lib.mkIf config.programs.droidcam.enable { + environment.systemPackages = [ pkgs.droidcam ]; + + boot.extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ]; + boot.kernelModules = [ "v4l2loopback" "snd-aloop" ]; + }; +} diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix new file mode 100644 index 00000000000..a448727be77 --- /dev/null +++ b/nixos/modules/programs/environment.nix @@ -0,0 +1,67 @@ +# This module defines a standard configuration for NixOS global environment. + +# Most of the stuff here should probably be moved elsewhere sometime. + +{ config, lib, ... }: + +with lib; + +let + + cfg = config.environment; + +in + +{ + + config = { + + environment.variables = + { NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix"; + # note: many programs exec() this directly, so default options for less must not + # be specified here; do so in the default value of programs.less.envVariables instead + PAGER = mkDefault "less"; + EDITOR = mkDefault "nano"; + XDG_CONFIG_DIRS = [ "/etc/xdg" ]; # needs to be before profile-relative paths to allow changes through environment.etc + }; + + # since we set PAGER to this above, make sure it's installed + programs.less.enable = true; + + environment.profiles = mkAfter + [ "/nix/var/nix/profiles/default" + "/run/current-system/sw" + ]; + + # TODO: move most of these elsewhere + environment.profileRelativeSessionVariables = + { PATH = [ "/bin" ]; + INFOPATH = [ "/info" "/share/info" ]; + KDEDIRS = [ "" ]; + QT_PLUGIN_PATH = [ "/lib/qt4/plugins" "/lib/kde4/plugins" ]; + QTWEBKIT_PLUGIN_PATH = [ "/lib/mozilla/plugins/" ]; + GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" "/lib/gtk-4.0" ]; + XDG_CONFIG_DIRS = [ "/etc/xdg" ]; + XDG_DATA_DIRS = [ "/share" ]; + MOZ_PLUGIN_PATH = [ "/lib/mozilla/plugins" ]; + LIBEXEC_PATH = [ "/lib/libexec" ]; + }; + + environment.pathsToLink = [ "/lib/gtk-2.0" "/lib/gtk-3.0" "/lib/gtk-4.0" ]; + + environment.extraInit = + '' + unset ASPELL_CONF + for i in ${concatStringsSep " " (reverseList cfg.profiles)} ; do + if [ -d "$i/lib/aspell" ]; then + export ASPELL_CONF="dict-dir $i/lib/aspell" + fi + done + + export NIX_USER_PROFILE_DIR="/nix/var/nix/profiles/per-user/$USER" + export NIX_PROFILES="${concatStringsSep " " (reverseList cfg.profiles)}" + ''; + + }; + +} diff --git a/nixos/modules/programs/evince.nix b/nixos/modules/programs/evince.nix new file mode 100644 index 00000000000..c033230afb1 --- /dev/null +++ b/nixos/modules/programs/evince.nix @@ -0,0 +1,51 @@ +# Evince. + +{ config, pkgs, lib, ... }: + +with lib; + +let cfg = config.programs.evince; + +in { + + # Added 2019-08-09 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome3" "evince" "enable" ] + [ "programs" "evince" "enable" ]) + ]; + + ###### interface + + options = { + + programs.evince = { + + enable = mkEnableOption + "Evince, the GNOME document viewer"; + + package = mkOption { + type = types.package; + default = pkgs.evince; + defaultText = literalExpression "pkgs.evince"; + description = "Evince derivation to use."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.programs.evince.enable { + + environment.systemPackages = [ cfg.package ]; + + services.dbus.packages = [ cfg.package ]; + + systemd.packages = [ cfg.package ]; + + }; + +} diff --git a/nixos/modules/programs/extra-container.nix b/nixos/modules/programs/extra-container.nix new file mode 100644 index 00000000000..c10ccd76916 --- /dev/null +++ b/nixos/modules/programs/extra-container.nix @@ -0,0 +1,17 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.programs.extra-container; +in { + options = { + programs.extra-container.enable = mkEnableOption '' + extra-container, a tool for running declarative NixOS containers + without host system rebuilds + ''; + }; + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.extra-container ]; + boot.extraSystemdUnitPaths = [ "/etc/systemd-mutable/system" ]; + }; +} diff --git a/nixos/modules/programs/feedbackd.nix b/nixos/modules/programs/feedbackd.nix new file mode 100644 index 00000000000..4194080c8a7 --- /dev/null +++ b/nixos/modules/programs/feedbackd.nix @@ -0,0 +1,33 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.programs.feedbackd; +in { + options = { + programs.feedbackd = { + enable = mkEnableOption '' + Whether to enable the feedbackd D-BUS service and udev rules. + + Your user needs to be in the `feedbackd` group to trigger effects. + ''; + package = mkOption { + description = '' + Which feedbackd package to use. + ''; + type = types.package; + default = pkgs.feedbackd; + defaultText = literalExpression "pkgs.feedbackd"; + }; + }; + }; + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + services.dbus.packages = [ cfg.package ]; + services.udev.packages = [ cfg.package ]; + + users.groups.feedbackd = {}; + }; +} diff --git a/nixos/modules/programs/file-roller.nix b/nixos/modules/programs/file-roller.nix new file mode 100644 index 00000000000..3c47d598165 --- /dev/null +++ b/nixos/modules/programs/file-roller.nix @@ -0,0 +1,48 @@ +# File Roller. + +{ config, pkgs, lib, ... }: + +with lib; + +let cfg = config.programs.file-roller; + +in { + + # Added 2019-08-09 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome3" "file-roller" "enable" ] + [ "programs" "file-roller" "enable" ]) + ]; + + ###### interface + + options = { + + programs.file-roller = { + + enable = mkEnableOption "File Roller, an archive manager for GNOME"; + + package = mkOption { + type = types.package; + default = pkgs.gnome.file-roller; + defaultText = literalExpression "pkgs.gnome.file-roller"; + description = "File Roller derivation to use."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + + services.dbus.packages = [ cfg.package ]; + + }; + +} diff --git a/nixos/modules/programs/firejail.nix b/nixos/modules/programs/firejail.nix new file mode 100644 index 00000000000..76b42168c19 --- /dev/null +++ b/nixos/modules/programs/firejail.nix @@ -0,0 +1,97 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.firejail; + + wrappedBins = pkgs.runCommand "firejail-wrapped-binaries" + { preferLocalBuild = true; + allowSubstitutes = false; + } + '' + mkdir -p $out/bin + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (command: value: + let + opts = if builtins.isAttrs value + then value + else { executable = value; profile = null; extraArgs = []; }; + args = lib.escapeShellArgs ( + opts.extraArgs + ++ (optional (opts.profile != null) "--profile=${toString opts.profile}") + ); + in + '' + cat <<_EOF >$out/bin/${command} + #! ${pkgs.runtimeShell} -e + exec /run/wrappers/bin/firejail ${args} -- ${toString opts.executable} "\$@" + _EOF + chmod 0755 $out/bin/${command} + '') cfg.wrappedBinaries)} + ''; + +in { + options.programs.firejail = { + enable = mkEnableOption "firejail"; + + wrappedBinaries = mkOption { + type = types.attrsOf (types.either types.path (types.submodule { + options = { + executable = mkOption { + type = types.path; + description = "Executable to run sandboxed"; + example = literalExpression ''"''${lib.getBin pkgs.firefox}/bin/firefox"''; + }; + profile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Profile to use"; + example = literalExpression ''"''${pkgs.firejail}/etc/firejail/firefox.profile"''; + }; + extraArgs = mkOption { + type = types.listOf types.str; + default = []; + description = "Extra arguments to pass to firejail"; + example = [ "--private=~/.firejail_home" ]; + }; + }; + })); + default = {}; + example = literalExpression '' + { + firefox = { + executable = "''${lib.getBin pkgs.firefox}/bin/firefox"; + profile = "''${pkgs.firejail}/etc/firejail/firefox.profile"; + }; + mpv = { + executable = "''${lib.getBin pkgs.mpv}/bin/mpv"; + profile = "''${pkgs.firejail}/etc/firejail/mpv.profile"; + }; + } + ''; + description = '' + Wrap the binaries in firejail and place them in the global path. + </para> + <para> + You will get file collisions if you put the actual application binary in + the global environment (such as by adding the application package to + <code>environment.systemPackages</code>), and applications started via + .desktop files are not wrapped if they specify the absolute path to the + binary. + ''; + }; + }; + + config = mkIf cfg.enable { + security.wrappers.firejail = + { setuid = true; + owner = "root"; + group = "root"; + source = "${lib.getBin pkgs.firejail}/bin/firejail"; + }; + + environment.systemPackages = [ pkgs.firejail ] ++ [ wrappedBins ]; + }; + + meta.maintainers = with maintainers; [ peterhoeg ]; +} diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix new file mode 100644 index 00000000000..8dd7101947f --- /dev/null +++ b/nixos/modules/programs/fish.nix @@ -0,0 +1,320 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfge = config.environment; + + cfg = config.programs.fish; + + fishAbbrs = concatStringsSep "\n" ( + mapAttrsFlatten (k: v: "abbr -ag ${k} ${escapeShellArg v}") + cfg.shellAbbrs + ); + + fishAliases = concatStringsSep "\n" ( + mapAttrsFlatten (k: v: "alias ${k} ${escapeShellArg v}") + (filterAttrs (k: v: v != null) cfg.shellAliases) + ); + + envShellInit = pkgs.writeText "shellInit" cfge.shellInit; + + envLoginShellInit = pkgs.writeText "loginShellInit" cfge.loginShellInit; + + envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit; + + sourceEnv = file: + if cfg.useBabelfish then + "source /etc/fish/${file}.fish" + else + '' + set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path + fenv source /etc/fish/foreign-env/${file} > /dev/null + set -e fish_function_path[1] + ''; + + babelfishTranslate = path: name: + pkgs.runCommand "${name}.fish" { + nativeBuildInputs = [ pkgs.babelfish ]; + } "${pkgs.babelfish}/bin/babelfish < ${path} > $out;"; + +in + +{ + + options = { + + programs.fish = { + + enable = mkOption { + default = false; + description = '' + Whether to configure fish as an interactive shell. + ''; + type = types.bool; + }; + + useBabelfish = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, the configured environment will be translated to native fish using <link xlink:href="https://github.com/bouk/babelfish">babelfish</link>. + Otherwise, <link xlink:href="https://github.com/oh-my-fish/plugin-foreign-env">foreign-env</link> will be used. + ''; + }; + + vendor.config.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether fish should source configuration snippets provided by other packages. + ''; + }; + + vendor.completions.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether fish should use completion files provided by other packages. + ''; + }; + + vendor.functions.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether fish should autoload fish functions provided by other packages. + ''; + }; + + shellAbbrs = mkOption { + default = {}; + example = { + gco = "git checkout"; + npu = "nix-prefetch-url"; + }; + description = '' + Set of fish abbreviations. + ''; + type = with types; attrsOf str; + }; + + shellAliases = mkOption { + default = {}; + description = '' + Set of aliases for fish shell, which overrides <option>environment.shellAliases</option>. + See <option>environment.shellAliases</option> for an option format description. + ''; + type = with types; attrsOf (nullOr (either str path)); + }; + + shellInit = mkOption { + default = ""; + description = '' + Shell script code called during fish shell initialisation. + ''; + type = types.lines; + }; + + loginShellInit = mkOption { + default = ""; + description = '' + Shell script code called during fish login shell initialisation. + ''; + type = types.lines; + }; + + interactiveShellInit = mkOption { + default = ""; + description = '' + Shell script code called during interactive fish shell initialisation. + ''; + type = types.lines; + }; + + promptInit = mkOption { + default = ""; + description = '' + Shell script code used to initialise fish prompt. + ''; + type = types.lines; + }; + + }; + + }; + + config = mkIf cfg.enable { + + programs.fish.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases; + + # Required for man completions + documentation.man.generateCaches = lib.mkDefault true; + + environment = mkMerge [ + (mkIf cfg.useBabelfish + { + etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment"; + etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit"; + etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit"; + etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit"; + }) + + (mkIf (!cfg.useBabelfish) + { + etc."fish/foreign-env/shellInit".source = envShellInit; + etc."fish/foreign-env/loginShellInit".source = envLoginShellInit; + etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit; + }) + + { + etc."fish/nixos-env-preinit.fish".text = + if cfg.useBabelfish + then '' + # source the NixOS environment config + if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ] + source /etc/fish/setEnvironment.fish + end + '' + else '' + # This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently + # unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish + set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions + + # source the NixOS environment config + if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ] + fenv source ${config.system.build.setEnvironment} + end + + # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish + set -e fish_function_path + ''; + } + + { + etc."fish/config.fish".text = '' + # /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically. + + # if we haven't sourced the general config, do it + if not set -q __fish_nixos_general_config_sourced + ${sourceEnv "shellInit"} + + ${cfg.shellInit} + + # and leave a note so we don't source this config section again from + # this very shell (children will source the general config anew) + set -g __fish_nixos_general_config_sourced 1 + end + + # if we haven't sourced the login config, do it + status --is-login; and not set -q __fish_nixos_login_config_sourced + and begin + ${sourceEnv "loginShellInit"} + + ${cfg.loginShellInit} + + # and leave a note so we don't source this config section again from + # this very shell (children will source the general config anew) + set -g __fish_nixos_login_config_sourced 1 + end + + # if we haven't sourced the interactive config, do it + status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced + and begin + ${fishAbbrs} + ${fishAliases} + + ${sourceEnv "interactiveShellInit"} + + ${cfg.promptInit} + ${cfg.interactiveShellInit} + + # and leave a note so we don't source this config section again from + # this very shell (children will source the general config anew, + # allowing configuration changes in, e.g, aliases, to propagate) + set -g __fish_nixos_interactive_config_sourced 1 + end + ''; + } + + { + etc."fish/generated_completions".source = + let + patchedGenerator = pkgs.stdenv.mkDerivation { + name = "fish_patched-completion-generator"; + srcs = [ + "${pkgs.fish}/share/fish/tools/create_manpage_completions.py" + "${pkgs.fish}/share/fish/tools/deroff.py" + ]; + unpackCmd = "cp $curSrc $(basename $curSrc)"; + sourceRoot = "."; + patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files + dontBuild = true; + installPhase = '' + mkdir -p $out + cp * $out/ + ''; + preferLocalBuild = true; + allowSubstitutes = false; + }; + generateCompletions = package: pkgs.runCommand + "${package.name}_fish-completions" + ( + { + inherit package; + preferLocalBuild = true; + allowSubstitutes = false; + } + // optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; } + ) + '' + mkdir -p $out + if [ -d $package/share/man ]; then + find $package/share/man -type f | xargs ${pkgs.python3.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null + fi + ''; + in + pkgs.buildEnv { + name = "system_fish-completions"; + ignoreCollisions = true; + paths = map generateCompletions config.environment.systemPackages; + }; + } + + # include programs that bring their own completions + { + pathsToLink = [] + ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d" + ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d" + ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d"; + } + + { systemPackages = [ pkgs.fish ]; } + + { + shells = [ + "/run/current-system/sw/bin/fish" + "${pkgs.fish}/bin/fish" + ]; + } + ]; + + programs.fish.interactiveShellInit = '' + # add completions generated by NixOS to $fish_complete_path + begin + # joins with null byte to acommodate all characters in paths, then respectively gets all paths before (exclusive) / after (inclusive) the first one including "generated_completions", + # splits by null byte, and then removes all empty lines produced by using 'string' + set -l prev (string join0 $fish_complete_path | string match --regex "^.*?(?=\x00[^\x00]*generated_completions.*)" | string split0 | string match -er ".") + set -l post (string join0 $fish_complete_path | string match --regex "[^\x00]*generated_completions.*" | string split0 | string match -er ".") + set fish_complete_path $prev "/etc/fish/generated_completions" $post + end + # prevent fish from generating completions on first run + if not test -d $__fish_user_data_dir/generated_completions + ${pkgs.coreutils}/bin/mkdir $__fish_user_data_dir/generated_completions + end + ''; + + }; + +} diff --git a/nixos/modules/programs/fish_completion-generator.patch b/nixos/modules/programs/fish_completion-generator.patch new file mode 100644 index 00000000000..fa207e484c9 --- /dev/null +++ b/nixos/modules/programs/fish_completion-generator.patch @@ -0,0 +1,14 @@ +--- a/create_manpage_completions.py ++++ b/create_manpage_completions.py +@@ -879,10 +879,6 @@ def parse_manpage_at_path(manpage_path, output_directory): + ) + return False + +- # Output the magic word Autogenerated so we can tell if we can overwrite this +- built_command_output.insert( +- 0, "# " + CMDNAME + "\n# Autogenerated from man page " + manpage_path +- ) + # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABLE PARSER! Was really using Type2 but reporting TypeDeroffManParser + + for line in built_command_output: + diff --git a/nixos/modules/programs/flashrom.nix b/nixos/modules/programs/flashrom.nix new file mode 100644 index 00000000000..f026c2e31cd --- /dev/null +++ b/nixos/modules/programs/flashrom.nix @@ -0,0 +1,26 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.flashrom; +in +{ + options.programs.flashrom = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Installs flashrom and configures udev rules for programmers + used by flashrom. Grants access to users in the "flashrom" + group. + ''; + }; + }; + + config = mkIf cfg.enable { + services.udev.packages = [ pkgs.flashrom ]; + environment.systemPackages = [ pkgs.flashrom ]; + users.groups.flashrom = { }; + }; +} diff --git a/nixos/modules/programs/flexoptix-app.nix b/nixos/modules/programs/flexoptix-app.nix new file mode 100644 index 00000000000..5e169be2d89 --- /dev/null +++ b/nixos/modules/programs/flexoptix-app.nix @@ -0,0 +1,25 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.flexoptix-app; +in { + options = { + programs.flexoptix-app = { + enable = mkEnableOption "FLEXOPTIX app + udev rules"; + + package = mkOption { + description = "FLEXOPTIX app package to use"; + type = types.package; + default = pkgs.flexoptix-app; + defaultText = literalExpression "pkgs.flexoptix-app"; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + services.udev.packages = [ cfg.package ]; + }; +} diff --git a/nixos/modules/programs/freetds.nix b/nixos/modules/programs/freetds.nix new file mode 100644 index 00000000000..d95c44d756a --- /dev/null +++ b/nixos/modules/programs/freetds.nix @@ -0,0 +1,61 @@ +# Global configuration for freetds environment. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.environment.freetds; + +in +{ + ###### interface + + options = { + + environment.freetds = mkOption { + type = types.attrsOf types.str; + default = {}; + example = literalExpression '' + { MYDATABASE = ''' + host = 10.0.2.100 + port = 1433 + tds version = 7.2 + '''; + } + ''; + description = + '' + Configure freetds database entries. Each attribute denotes + a section within freetds.conf, and the value (a string) is the config + content for that section. When at least one entry is configured + the global environment variables FREETDSCONF, FREETDS and SYBASE + will be configured to allow the programs that use freetds to find the + library and config. + ''; + + }; + + }; + + ###### implementation + + config = mkIf (length (attrNames cfg) > 0) { + + environment.variables.FREETDSCONF = "/etc/freetds.conf"; + environment.variables.FREETDS = "/etc/freetds.conf"; + environment.variables.SYBASE = "${pkgs.freetds}"; + + environment.etc."freetds.conf" = { text = + (concatStrings (mapAttrsToList (name: value: + '' + [${name}] + ${value} + '' + ) cfg)); + }; + + }; + +} diff --git a/nixos/modules/programs/fuse.nix b/nixos/modules/programs/fuse.nix new file mode 100644 index 00000000000..c15896efbb5 --- /dev/null +++ b/nixos/modules/programs/fuse.nix @@ -0,0 +1,37 @@ +{ config, lib, ... }: + +with lib; + +let + cfg = config.programs.fuse; +in { + meta.maintainers = with maintainers; [ primeos ]; + + options.programs.fuse = { + mountMax = mkOption { + # In the C code it's an "int" (i.e. signed and at least 16 bit), but + # negative numbers obviously make no sense: + type = types.ints.between 0 32767; # 2^15 - 1 + default = 1000; + description = '' + Set the maximum number of FUSE mounts allowed to non-root users. + ''; + }; + + userAllowOther = mkOption { + type = types.bool; + default = false; + description = '' + Allow non-root users to specify the allow_other or allow_root mount + options, see mount.fuse3(8). + ''; + }; + }; + + config = { + environment.etc."fuse.conf".text = '' + ${optionalString (!cfg.userAllowOther) "#"}user_allow_other + mount_max = ${toString cfg.mountMax} + ''; + }; +} diff --git a/nixos/modules/programs/gamemode.nix b/nixos/modules/programs/gamemode.nix new file mode 100644 index 00000000000..a377a1619aa --- /dev/null +++ b/nixos/modules/programs/gamemode.nix @@ -0,0 +1,98 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.gamemode; + settingsFormat = pkgs.formats.ini { }; + configFile = settingsFormat.generate "gamemode.ini" cfg.settings; +in +{ + options = { + programs.gamemode = { + enable = mkEnableOption "GameMode to optimise system performance on demand"; + + enableRenice = mkEnableOption "CAP_SYS_NICE on gamemoded to support lowering process niceness" // { + default = true; + }; + + settings = mkOption { + type = settingsFormat.type; + default = {}; + description = '' + System-wide configuration for GameMode (/etc/gamemode.ini). + See gamemoded(8) man page for available settings. + ''; + example = literalExpression '' + { + general = { + renice = 10; + }; + + # Warning: GPU optimisations have the potential to damage hardware + gpu = { + apply_gpu_optimisations = "accept-responsibility"; + gpu_device = 0; + amd_performance_level = "high"; + }; + + custom = { + start = "''${pkgs.libnotify}/bin/notify-send 'GameMode started'"; + end = "''${pkgs.libnotify}/bin/notify-send 'GameMode ended'"; + }; + } + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment = { + systemPackages = [ pkgs.gamemode ]; + etc."gamemode.ini".source = configFile; + }; + + security = { + polkit.enable = true; + wrappers = mkIf cfg.enableRenice { + gamemoded = { + owner = "root"; + group = "root"; + source = "${pkgs.gamemode}/bin/gamemoded"; + capabilities = "cap_sys_nice+ep"; + }; + }; + }; + + systemd = { + packages = [ pkgs.gamemode ]; + user.services.gamemoded = { + # The upstream service already defines this, but doesn't get applied. + # See https://github.com/NixOS/nixpkgs/issues/81138 + wantedBy = [ "default.target" ]; + + # Use pkexec from the security wrappers to allow users to + # run libexec/cpugovctl & libexec/gpuclockctl as root with + # the the actions defined in share/polkit-1/actions. + # + # This uses a link farm to make sure other wrapped executables + # aren't included in PATH. + environment.PATH = mkForce (pkgs.linkFarm "pkexec" [ + { + name = "pkexec"; + path = "${config.security.wrapperDir}/pkexec"; + } + ]); + + serviceConfig.ExecStart = mkIf cfg.enableRenice [ + "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it. + "${config.security.wrapperDir}/gamemoded" + ]; + }; + }; + }; + + meta = { + maintainers = with maintainers; [ kira-bruneau ]; + }; +} diff --git a/nixos/modules/programs/geary.nix b/nixos/modules/programs/geary.nix new file mode 100644 index 00000000000..407680c30dc --- /dev/null +++ b/nixos/modules/programs/geary.nix @@ -0,0 +1,24 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.geary; + +in { + meta = { + maintainers = teams.gnome.members; + }; + + options = { + programs.geary.enable = mkEnableOption "Geary, a Mail client for GNOME 3"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.gnome.geary ]; + programs.dconf.enable = true; + services.gnome.gnome-keyring.enable = true; + services.gnome.gnome-online-accounts.enable = true; + }; +} + diff --git a/nixos/modules/programs/git.nix b/nixos/modules/programs/git.nix new file mode 100644 index 00000000000..06ce374b199 --- /dev/null +++ b/nixos/modules/programs/git.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.git; +in + +{ + options = { + programs.git = { + enable = mkEnableOption "git"; + + package = mkOption { + type = types.package; + default = pkgs.git; + defaultText = literalExpression "pkgs.git"; + example = literalExpression "pkgs.gitFull"; + description = "The git package to use"; + }; + + config = mkOption { + type = with types; attrsOf (attrsOf anything); + default = { }; + example = { + init.defaultBranch = "main"; + url."https://github.com/".insteadOf = [ "gh:" "github:" ]; + }; + description = '' + Configuration to write to /etc/gitconfig. See the CONFIGURATION FILE + section of git-config(1) for more information. + ''; + }; + + lfs = { + enable = mkEnableOption "git-lfs"; + + package = mkOption { + type = types.package; + default = pkgs.git-lfs; + defaultText = literalExpression "pkgs.git-lfs"; + description = "The git-lfs package to use"; + }; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + environment.etc.gitconfig = mkIf (cfg.config != {}) { + text = generators.toGitINI cfg.config; + }; + }) + (mkIf (cfg.enable && cfg.lfs.enable) { + environment.systemPackages = [ cfg.lfs.package ]; + programs.git.config = { + filter.lfs = { + clean = "git-lfs clean -- %f"; + smudge = "git-lfs smudge -- %f"; + process = "git-lfs filter-process"; + required = true; + }; + }; + }) + ]; + + meta.maintainers = with maintainers; [ figsoda ]; +} diff --git a/nixos/modules/programs/gnome-disks.nix b/nixos/modules/programs/gnome-disks.nix new file mode 100644 index 00000000000..4b128b47126 --- /dev/null +++ b/nixos/modules/programs/gnome-disks.nix @@ -0,0 +1,50 @@ +# GNOME Disks. + +{ config, pkgs, lib, ... }: + +with lib; + +{ + + meta = { + maintainers = teams.gnome.members; + }; + + # Added 2019-08-09 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome3" "gnome-disks" "enable" ] + [ "programs" "gnome-disks" "enable" ]) + ]; + + ###### interface + + options = { + + programs.gnome-disks = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable GNOME Disks daemon, a program designed to + be a UDisks2 graphical front-end. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.programs.gnome-disks.enable { + + environment.systemPackages = [ pkgs.gnome.gnome-disk-utility ]; + + services.dbus.packages = [ pkgs.gnome.gnome-disk-utility ]; + + }; + +} diff --git a/nixos/modules/programs/gnome-documents.nix b/nixos/modules/programs/gnome-documents.nix new file mode 100644 index 00000000000..43ad3163efd --- /dev/null +++ b/nixos/modules/programs/gnome-documents.nix @@ -0,0 +1,54 @@ +# GNOME Documents. + +{ config, pkgs, lib, ... }: + +with lib; + +{ + + meta = { + maintainers = teams.gnome.members; + }; + + # Added 2019-08-09 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome" "gnome-documents" "enable" ] + [ "programs" "gnome-documents" "enable" ]) + ]; + + ###### interface + + options = { + + programs.gnome-documents = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable GNOME Documents, a document + manager application for GNOME. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.programs.gnome-documents.enable { + + environment.systemPackages = [ pkgs.gnome.gnome-documents ]; + + services.dbus.packages = [ pkgs.gnome.gnome-documents ]; + + services.gnome.gnome-online-accounts.enable = true; + + services.gnome.gnome-online-miners.enable = true; + + }; + +} diff --git a/nixos/modules/programs/gnome-terminal.nix b/nixos/modules/programs/gnome-terminal.nix new file mode 100644 index 00000000000..71a6b217880 --- /dev/null +++ b/nixos/modules/programs/gnome-terminal.nix @@ -0,0 +1,38 @@ +# GNOME Terminal. + +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.programs.gnome-terminal; + +in + +{ + + meta = { + maintainers = teams.gnome.members; + }; + + # Added 2019-08-19 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome3" "gnome-terminal-server" "enable" ] + [ "programs" "gnome-terminal" "enable" ]) + ]; + + options = { + programs.gnome-terminal.enable = mkEnableOption "GNOME Terminal"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.gnome.gnome-terminal ]; + services.dbus.packages = [ pkgs.gnome.gnome-terminal ]; + systemd.packages = [ pkgs.gnome.gnome-terminal ]; + + programs.bash.vteIntegration = true; + programs.zsh.vteIntegration = true; + }; +} diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix new file mode 100644 index 00000000000..b41f30287ea --- /dev/null +++ b/nixos/modules/programs/gnupg.nix @@ -0,0 +1,154 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.gnupg; + + xserverCfg = config.services.xserver; + + defaultPinentryFlavor = + if xserverCfg.desktopManager.lxqt.enable + || xserverCfg.desktopManager.plasma5.enable then + "qt" + else if xserverCfg.desktopManager.xfce.enable then + "gtk2" + else if xserverCfg.enable || config.programs.sway.enable then + "gnome3" + else + null; + +in + +{ + + options.programs.gnupg = { + package = mkOption { + type = types.package; + default = pkgs.gnupg; + defaultText = literalExpression "pkgs.gnupg"; + description = '' + The gpg package that should be used. + ''; + }; + + agent.enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables GnuPG agent with socket-activation for every user session. + ''; + }; + + agent.enableSSHSupport = mkOption { + type = types.bool; + default = false; + description = '' + Enable SSH agent support in GnuPG agent. Also sets SSH_AUTH_SOCK + environment variable correctly. This will disable socket-activation + and thus always start a GnuPG agent per user session. + ''; + }; + + agent.enableExtraSocket = mkOption { + type = types.bool; + default = false; + description = '' + Enable extra socket for GnuPG agent. + ''; + }; + + agent.enableBrowserSocket = mkOption { + type = types.bool; + default = false; + description = '' + Enable browser socket for GnuPG agent. + ''; + }; + + agent.pinentryFlavor = mkOption { + type = types.nullOr (types.enum pkgs.pinentry.flavors); + example = "gnome3"; + default = defaultPinentryFlavor; + defaultText = literalDocBook ''matching the configured desktop environment''; + description = '' + Which pinentry interface to use. If not null, the path to the + pinentry binary will be passed to gpg-agent via commandline and + thus overrides the pinentry option in gpg-agent.conf in the user's + home directory. + If not set at all, it'll pick an appropriate flavor depending on the + system configuration (qt flavor for lxqt and plasma5, gtk2 for xfce + 4.12, gnome3 on all other systems with X enabled, ncurses otherwise). + ''; + }; + + dirmngr.enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables GnuPG network certificate management daemon with socket-activation for every user session. + ''; + }; + }; + + config = mkIf cfg.agent.enable { + # This overrides the systemd user unit shipped with the gnupg package + systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) { + serviceConfig.ExecStart = [ "" '' + ${cfg.package}/bin/gpg-agent --supervised \ + --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry + '' ]; + }; + + systemd.user.sockets.gpg-agent = { + wantedBy = [ "sockets.target" ]; + }; + + systemd.user.sockets.gpg-agent-ssh = mkIf cfg.agent.enableSSHSupport { + wantedBy = [ "sockets.target" ]; + }; + + systemd.user.sockets.gpg-agent-extra = mkIf cfg.agent.enableExtraSocket { + wantedBy = [ "sockets.target" ]; + }; + + systemd.user.sockets.gpg-agent-browser = mkIf cfg.agent.enableBrowserSocket { + wantedBy = [ "sockets.target" ]; + }; + + systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable { + wantedBy = [ "sockets.target" ]; + }; + + services.dbus.packages = mkIf (cfg.agent.pinentryFlavor == "gnome3") [ pkgs.gcr ]; + + environment.systemPackages = with pkgs; [ cfg.package ]; + systemd.packages = [ cfg.package ]; + + environment.interactiveShellInit = '' + # Bind gpg-agent to this TTY if gpg commands are used. + export GPG_TTY=$(tty) + + '' + (optionalString cfg.agent.enableSSHSupport '' + # SSH agent protocol doesn't support changing TTYs, so bind the agent + # to every new TTY. + ${cfg.package}/bin/gpg-connect-agent --quiet updatestartuptty /bye > /dev/null + ''); + + environment.extraInit = mkIf cfg.agent.enableSSHSupport '' + if [ -z "$SSH_AUTH_SOCK" ]; then + export SSH_AUTH_SOCK=$(${cfg.package}/bin/gpgconf --list-dirs agent-ssh-socket) + fi + ''; + + assertions = [ + { assertion = cfg.agent.enableSSHSupport -> !config.programs.ssh.startAgent; + message = "You can't use ssh-agent and GnuPG agent with SSH support enabled at the same time!"; + } + ]; + }; + + # uses attributes of the linked package + meta.buildDocsInSandbox = false; +} diff --git a/nixos/modules/programs/gpaste.nix b/nixos/modules/programs/gpaste.nix new file mode 100644 index 00000000000..cff2fb8d003 --- /dev/null +++ b/nixos/modules/programs/gpaste.nix @@ -0,0 +1,36 @@ +# GPaste. +{ config, lib, pkgs, ... }: + +with lib; + +{ + + # Added 2019-08-09 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome3" "gpaste" "enable" ] + [ "programs" "gpaste" "enable" ]) + ]; + + ###### interface + options = { + programs.gpaste = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable GPaste, a clipboard manager. + ''; + }; + }; + }; + + ###### implementation + config = mkIf config.programs.gpaste.enable { + environment.systemPackages = [ pkgs.gnome.gpaste ]; + services.dbus.packages = [ pkgs.gnome.gpaste ]; + systemd.packages = [ pkgs.gnome.gpaste ]; + # gnome-control-center crashes in Keyboard Shortcuts pane without the GSettings schemas. + services.xserver.desktopManager.gnome.sessionPath = [ pkgs.gnome.gpaste ]; + }; +} diff --git a/nixos/modules/programs/gphoto2.nix b/nixos/modules/programs/gphoto2.nix new file mode 100644 index 00000000000..93923ff3133 --- /dev/null +++ b/nixos/modules/programs/gphoto2.nix @@ -0,0 +1,30 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta.maintainers = [ maintainers.league ]; + + ###### interface + options = { + programs.gphoto2 = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to configure system to use gphoto2. + To grant digital camera access to a user, the user must + be part of the camera group: + <code>users.users.alice.extraGroups = ["camera"];</code> + ''; + }; + }; + }; + + ###### implementation + config = mkIf config.programs.gphoto2.enable { + services.udev.packages = [ pkgs.libgphoto2 ]; + environment.systemPackages = [ pkgs.gphoto2 ]; + users.groups.camera = {}; + }; +} diff --git a/nixos/modules/programs/hamster.nix b/nixos/modules/programs/hamster.nix new file mode 100644 index 00000000000..0bb56ad7ff3 --- /dev/null +++ b/nixos/modules/programs/hamster.nix @@ -0,0 +1,15 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta.maintainers = pkgs.hamster.meta.maintainers; + + options.programs.hamster.enable = + mkEnableOption "hamster, a time tracking program"; + + config = lib.mkIf config.programs.hamster.enable { + environment.systemPackages = [ pkgs.hamster ]; + services.dbus.packages = [ pkgs.hamster ]; + }; +} diff --git a/nixos/modules/programs/htop.nix b/nixos/modules/programs/htop.nix new file mode 100644 index 00000000000..5c197838e47 --- /dev/null +++ b/nixos/modules/programs/htop.nix @@ -0,0 +1,58 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.htop; + + fmt = value: + if isList value then concatStringsSep " " (map fmt value) else + if isString value then value else + if isBool value || isInt value then toString value else + throw "Unrecognized type ${typeOf value} in htop settings"; + +in + +{ + + options.programs.htop = { + package = mkOption { + type = types.package; + default = pkgs.htop; + defaultText = "pkgs.htop"; + description = '' + The htop package that should be used. + ''; + }; + + enable = mkEnableOption "htop process monitor"; + + settings = mkOption { + type = with types; attrsOf (oneOf [ str int bool (listOf (oneOf [ str int bool ])) ]); + default = {}; + example = { + hide_kernel_threads = true; + hide_userland_threads = true; + }; + description = '' + Extra global default configuration for htop + which is read on first startup only. + Htop subsequently uses ~/.config/htop/htoprc + as configuration source. + ''; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ + cfg.package + ]; + + environment.etc."htoprc".text = '' + # Global htop configuration + # To change set: programs.htop.settings.KEY = VALUE; + '' + concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${fmt value}") cfg.settings); + }; + +} diff --git a/nixos/modules/programs/iftop.nix b/nixos/modules/programs/iftop.nix new file mode 100644 index 00000000000..c74714a9a6d --- /dev/null +++ b/nixos/modules/programs/iftop.nix @@ -0,0 +1,20 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.iftop; +in { + options = { + programs.iftop.enable = mkEnableOption "iftop + setcap wrapper"; + }; + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.iftop ]; + security.wrappers.iftop = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = "${pkgs.iftop}/bin/iftop"; + }; + }; +} diff --git a/nixos/modules/programs/iotop.nix b/nixos/modules/programs/iotop.nix new file mode 100644 index 00000000000..b7c1c69f9dd --- /dev/null +++ b/nixos/modules/programs/iotop.nix @@ -0,0 +1,19 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.iotop; +in { + options = { + programs.iotop.enable = mkEnableOption "iotop + setcap wrapper"; + }; + config = mkIf cfg.enable { + security.wrappers.iotop = { + owner = "root"; + group = "root"; + capabilities = "cap_net_admin+p"; + source = "${pkgs.iotop}/bin/iotop"; + }; + }; +} diff --git a/nixos/modules/programs/java.nix b/nixos/modules/programs/java.nix new file mode 100644 index 00000000000..4e4e0629e5d --- /dev/null +++ b/nixos/modules/programs/java.nix @@ -0,0 +1,58 @@ +# This module provides JAVA_HOME, with a different way to install java +# system-wide. + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.java; +in + +{ + + options = { + + programs.java = { + + enable = mkEnableOption "java" // { + description = '' + Install and setup the Java development kit. + <note> + <para>This adds JAVA_HOME to the global environment, by sourcing the + jdk's setup-hook on shell init. It is equivalent to starting a shell + through 'nix-shell -p jdk', or roughly the following system-wide + configuration: + </para> + <programlisting> + environment.variables.JAVA_HOME = ''${pkgs.jdk.home}/lib/openjdk; + environment.systemPackages = [ pkgs.jdk ]; + </programlisting> + </note> + ''; + }; + + package = mkOption { + default = pkgs.jdk; + defaultText = literalExpression "pkgs.jdk"; + description = '' + Java package to install. Typical values are pkgs.jdk or pkgs.jre. + ''; + type = types.package; + }; + + }; + + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + + environment.shellInit = '' + test -e ${cfg.package}/nix-support/setup-hook && source ${cfg.package}/nix-support/setup-hook + ''; + + }; + +} diff --git a/nixos/modules/programs/k40-whisperer.nix b/nixos/modules/programs/k40-whisperer.nix new file mode 100644 index 00000000000..3163e45f57e --- /dev/null +++ b/nixos/modules/programs/k40-whisperer.nix @@ -0,0 +1,40 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.k40-whisperer; + pkg = cfg.package.override { + udevGroup = cfg.group; + }; +in +{ + options.programs.k40-whisperer = { + enable = mkEnableOption "K40-Whisperer"; + + group = mkOption { + type = types.str; + description = '' + Group assigned to the device when connected. + ''; + default = "k40"; + }; + + package = mkOption { + type = types.package; + default = pkgs.k40-whisperer; + defaultText = literalExpression "pkgs.k40-whisperer"; + example = literalExpression "pkgs.k40-whisperer"; + description = '' + K40 Whisperer package to use. + ''; + }; + }; + + config = mkIf cfg.enable { + users.groups.${cfg.group} = {}; + + environment.systemPackages = [ pkg ]; + services.udev.packages = [ pkg ]; + }; +} diff --git a/nixos/modules/programs/kbdlight.nix b/nixos/modules/programs/kbdlight.nix new file mode 100644 index 00000000000..8a2a0057cf2 --- /dev/null +++ b/nixos/modules/programs/kbdlight.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.kbdlight; + +in +{ + options.programs.kbdlight.enable = mkEnableOption "kbdlight"; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.kbdlight ]; + security.wrappers.kbdlight = + { setuid = true; + owner = "root"; + group = "root"; + source = "${pkgs.kbdlight.out}/bin/kbdlight"; + }; + }; +} diff --git a/nixos/modules/programs/kclock.nix b/nixos/modules/programs/kclock.nix new file mode 100644 index 00000000000..42d81d2798b --- /dev/null +++ b/nixos/modules/programs/kclock.nix @@ -0,0 +1,13 @@ +{ lib, pkgs, config, ... }: +with lib; +let + cfg = config.programs.kclock; + kclockPkg = pkgs.libsForQt5.kclock; +in { + options.programs.kclock = { enable = mkEnableOption "Enable KClock"; }; + + config = mkIf cfg.enable { + services.dbus.packages = [ kclockPkg ]; + environment.systemPackages = [ kclockPkg ]; + }; +} diff --git a/nixos/modules/programs/kdeconnect.nix b/nixos/modules/programs/kdeconnect.nix new file mode 100644 index 00000000000..df698e84dd7 --- /dev/null +++ b/nixos/modules/programs/kdeconnect.nix @@ -0,0 +1,35 @@ +{ config, pkgs, lib, ... }: +with lib; +{ + options.programs.kdeconnect = { + enable = mkEnableOption '' + kdeconnect. + + Note that it will open the TCP and UDP port from + 1714 to 1764 as they are needed for it to function properly. + You can use the <option>package</option> to use + <code>gnomeExtensions.gsconnect</code> as an alternative + implementation if you use Gnome. + ''; + package = mkOption { + default = pkgs.kdeconnect; + defaultText = literalExpression "pkgs.kdeconnect"; + type = types.package; + example = literalExpression "pkgs.gnomeExtensions.gsconnect"; + description = '' + The package providing the implementation for kdeconnect. + ''; + }; + }; + config = + let + cfg = config.programs.kdeconnect; + in + mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + networking.firewall = rec { + allowedTCPPortRanges = [ { from = 1714; to = 1764; } ]; + allowedUDPPortRanges = allowedTCPPortRanges; + }; + }; +} diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix new file mode 100644 index 00000000000..794146b19fa --- /dev/null +++ b/nixos/modules/programs/less.nix @@ -0,0 +1,134 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.less; + + configText = if (cfg.configFile != null) then (builtins.readFile cfg.configFile) else '' + #command + ${concatStringsSep "\n" + (mapAttrsToList (command: action: "${command} ${action}") cfg.commands) + } + ${if cfg.clearDefaultCommands then "#stop" else ""} + + #line-edit + ${concatStringsSep "\n" + (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys) + } + + #env + ${concatStringsSep "\n" + (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables) + } + ''; + + lessKey = pkgs.writeText "lessconfig" configText; + +in + +{ + options = { + + programs.less = { + + # note that environment.nix sets PAGER=less, and + # therefore also enables this module + enable = mkEnableOption "less"; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + example = literalExpression ''"''${pkgs.my-configs}/lesskey"''; + description = '' + Path to lesskey configuration file. + + <option>configFile</option> takes precedence over <option>commands</option>, + <option>clearDefaultCommands</option>, <option>lineEditingKeys</option>, and + <option>envVariables</option>. + ''; + }; + + commands = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { + h = "noaction 5\\e("; + l = "noaction 5\\e)"; + }; + description = "Defines new command keys."; + }; + + clearDefaultCommands = mkOption { + type = types.bool; + default = false; + description = '' + Clear all default commands. + You should remember to set the quit key. + Otherwise you will not be able to leave less without killing it. + ''; + }; + + lineEditingKeys = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { + e = "abort"; + }; + description = "Defines new line-editing keys."; + }; + + envVariables = mkOption { + type = types.attrsOf types.str; + default = { + LESS = "-R"; + }; + example = { + LESS = "--quit-if-one-screen"; + }; + description = "Defines environment variables."; + }; + + lessopen = mkOption { + type = types.nullOr types.str; + default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s"; + defaultText = literalExpression ''"|''${pkgs.lesspipe}/bin/lesspipe.sh %s"''; + description = '' + Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed. + ''; + }; + + lessclose = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may perform any desired clean-up action (such as deleting the replacement file created by LESSOPEN). + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.less ]; + + environment.variables = { + LESSKEYIN_SYSTEM = toString lessKey; + } // optionalAttrs (cfg.lessopen != null) { + LESSOPEN = cfg.lessopen; + } // optionalAttrs (cfg.lessclose != null) { + LESSCLOSE = cfg.lessclose; + }; + + warnings = optional ( + cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands)) + ) '' + config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting. + Consider adding a binding for 'quit'. + ''; + }; + + meta.maintainers = with maintainers; [ johnazoidberg ]; + +} diff --git a/nixos/modules/programs/liboping.nix b/nixos/modules/programs/liboping.nix new file mode 100644 index 00000000000..4433f9767d6 --- /dev/null +++ b/nixos/modules/programs/liboping.nix @@ -0,0 +1,24 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.liboping; +in { + options.programs.liboping = { + enable = mkEnableOption "liboping"; + }; + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ liboping ]; + security.wrappers = mkMerge (map ( + exec: { + "${exec}" = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = "${pkgs.liboping}/bin/${exec}"; + }; + } + ) [ "oping" "noping" ]); + }; +} diff --git a/nixos/modules/programs/light.nix b/nixos/modules/programs/light.nix new file mode 100644 index 00000000000..9f2a03e7e76 --- /dev/null +++ b/nixos/modules/programs/light.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.light; + +in +{ + options = { + programs.light = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to install Light backlight control command + and udev rules granting access to members of the "video" group. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.light ]; + services.udev.packages = [ pkgs.light ]; + }; +} diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix new file mode 100644 index 00000000000..6e90e7669ac --- /dev/null +++ b/nixos/modules/programs/mininet.nix @@ -0,0 +1,39 @@ +# Global configuration for mininet +# kernel must have NETNS/VETH/SCHED +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.mininet; + + generatedPath = with pkgs; makeSearchPath "bin" [ + iperf ethtool iproute2 socat + ]; + + pyEnv = pkgs.python.withPackages(ps: [ ps.mininet-python ]); + + mnexecWrapped = pkgs.runCommand "mnexec-wrapper" + { buildInputs = [ pkgs.makeWrapper pkgs.pythonPackages.wrapPython ]; } + '' + makeWrapper ${pkgs.mininet}/bin/mnexec \ + $out/bin/mnexec \ + --prefix PATH : "${generatedPath}" + + ln -s ${pyEnv}/bin/mn $out/bin/mn + + # mn errors out without a telnet binary + # pkgs.telnet brings an undesired ifconfig into PATH see #43105 + ln -s ${pkgs.telnet}/bin/telnet $out/bin/telnet + ''; +in +{ + options.programs.mininet.enable = mkEnableOption "Mininet"; + + config = mkIf cfg.enable { + + virtualisation.vswitch.enable = true; + + environment.systemPackages = [ mnexecWrapped ]; + }; +} diff --git a/nixos/modules/programs/mosh.nix b/nixos/modules/programs/mosh.nix new file mode 100644 index 00000000000..e08099e21a0 --- /dev/null +++ b/nixos/modules/programs/mosh.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.mosh; + +in +{ + options.programs.mosh = { + enable = mkOption { + description = '' + Whether to enable mosh. Note, this will open ports in your firewall! + ''; + default = false; + type = lib.types.bool; + }; + withUtempter = mkOption { + description = '' + Whether to enable libutempter for mosh. + This is required so that mosh can write to /var/run/utmp (which can be queried with `who` to display currently connected user sessions). + Note, this will add a guid wrapper for the group utmp! + ''; + default = true; + type = lib.types.bool; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ mosh ]; + networking.firewall.allowedUDPPortRanges = [ { from = 60000; to = 61000; } ]; + security.wrappers = mkIf cfg.withUtempter { + utempter = { + source = "${pkgs.libutempter}/lib/utempter/utempter"; + owner = "root"; + group = "utmp"; + setuid = false; + setgid = true; + }; + }; + }; +} diff --git a/nixos/modules/programs/msmtp.nix b/nixos/modules/programs/msmtp.nix new file mode 100644 index 00000000000..9c067bdc969 --- /dev/null +++ b/nixos/modules/programs/msmtp.nix @@ -0,0 +1,106 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.msmtp; + +in { + meta.maintainers = with maintainers; [ pacien ]; + + options = { + programs.msmtp = { + enable = mkEnableOption "msmtp - an SMTP client"; + + setSendmail = mkOption { + type = types.bool; + default = true; + description = '' + Whether to set the system sendmail to msmtp's. + ''; + }; + + defaults = mkOption { + type = types.attrs; + default = {}; + example = { + aliases = "/etc/aliases"; + port = 587; + tls = true; + }; + description = '' + Default values applied to all accounts. + See msmtp(1) for the available options. + ''; + }; + + accounts = mkOption { + type = with types; attrsOf attrs; + default = {}; + example = { + "default" = { + host = "smtp.example"; + auth = true; + user = "someone"; + passwordeval = "cat /secrets/password.txt"; + }; + }; + description = '' + Named accounts and their respective configurations. + The special name "default" allows a default account to be defined. + See msmtp(1) for the available options. + + Use `programs.msmtp.extraConfig` instead of this attribute set-based + option if ordered account inheritance is needed. + + It is advised to use the `passwordeval` setting to read the password + from a secret file to avoid having it written in the world-readable + nix store. The password file must end with a newline (`\n`). + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to add to the msmtp configuration verbatim. + See msmtp(1) for the syntax and available options. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.msmtp ]; + + services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail { + program = "sendmail"; + source = "${pkgs.msmtp}/bin/sendmail"; + setuid = false; + setgid = false; + owner = "root"; + group = "root"; + }; + + environment.etc."msmtprc".text = let + mkValueString = v: + if v == true then "on" + else if v == false then "off" + else generators.mkValueStringDefault {} v; + mkKeyValueString = k: v: "${k} ${mkValueString v}"; + mkInnerSectionString = + attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValueString attrs); + mkAccountString = name: attrs: '' + account ${name} + ${mkInnerSectionString attrs} + ''; + in '' + defaults + ${mkInnerSectionString cfg.defaults} + + ${concatStringsSep "\n" (mapAttrsToList mkAccountString cfg.accounts)} + + ${cfg.extraConfig} + ''; + }; +} diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix new file mode 100644 index 00000000000..3cffe0fd8b2 --- /dev/null +++ b/nixos/modules/programs/mtr.nix @@ -0,0 +1,41 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.mtr; + +in { + options = { + programs.mtr = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add mtr to the global environment and configure a + setcap wrapper for it. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.mtr; + defaultText = literalExpression "pkgs.mtr"; + description = '' + The package to use. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ cfg.package ]; + + security.wrappers.mtr-packet = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = "${cfg.package}/bin/mtr-packet"; + }; + }; +} diff --git a/nixos/modules/programs/nano.nix b/nixos/modules/programs/nano.nix new file mode 100644 index 00000000000..5837dd46d7c --- /dev/null +++ b/nixos/modules/programs/nano.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.nano; + LF = "\n"; +in + +{ + ###### interface + + options = { + programs.nano = { + + nanorc = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + The system-wide nano configuration. + See <citerefentry><refentrytitle>nanorc</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + ''; + example = '' + set nowrap + set tabstospaces + set tabsize 2 + ''; + }; + syntaxHighlight = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to enable syntax highlight for various languages."; + }; + }; + }; + + ###### implementation + + config = lib.mkIf (cfg.nanorc != "" || cfg.syntaxHighlight) { + environment.etc.nanorc.text = lib.concatStrings [ cfg.nanorc + (lib.optionalString cfg.syntaxHighlight ''${LF}include "${pkgs.nano}/share/nano/*.nanorc"'') ]; + }; + +} diff --git a/nixos/modules/programs/nbd.nix b/nixos/modules/programs/nbd.nix new file mode 100644 index 00000000000..fea9bc1ff71 --- /dev/null +++ b/nixos/modules/programs/nbd.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.nbd; +in +{ + options = { + programs.nbd = { + enable = mkEnableOption "Network Block Device (nbd) support"; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ nbd ]; + boot.kernelModules = [ "nbd" ]; + }; +} diff --git a/nixos/modules/programs/neovim.nix b/nixos/modules/programs/neovim.nix new file mode 100644 index 00000000000..4649662542d --- /dev/null +++ b/nixos/modules/programs/neovim.nix @@ -0,0 +1,166 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.neovim; + + runtime' = filter (f: f.enable) (attrValues cfg.runtime); + + runtime = pkgs.linkFarm "neovim-runtime" (map (x: { name = x.target; path = x.source; }) runtime'); + +in { + options.programs.neovim = { + enable = mkEnableOption "Neovim"; + + defaultEditor = mkOption { + type = types.bool; + default = false; + description = '' + When enabled, installs neovim and configures neovim to be the default editor + using the EDITOR environment variable. + ''; + }; + + viAlias = mkOption { + type = types.bool; + default = false; + description = '' + Symlink <command>vi</command> to <command>nvim</command> binary. + ''; + }; + + vimAlias = mkOption { + type = types.bool; + default = false; + description = '' + Symlink <command>vim</command> to <command>nvim</command> binary. + ''; + }; + + withRuby = mkOption { + type = types.bool; + default = true; + description = "Enable Ruby provider."; + }; + + withPython3 = mkOption { + type = types.bool; + default = true; + description = "Enable Python 3 provider."; + }; + + withNodeJs = mkOption { + type = types.bool; + default = false; + description = "Enable Node provider."; + }; + + configure = mkOption { + type = types.attrs; + default = {}; + example = literalExpression '' + { + customRC = ''' + " here your custom configuration goes! + '''; + packages.myVimPackage = with pkgs.vimPlugins; { + # loaded on launch + start = [ fugitive ]; + # manually loadable by calling `:packadd $plugin-name` + opt = [ ]; + }; + } + ''; + description = '' + Generate your init file from your list of plugins and custom commands. + Neovim will then be wrapped to load <command>nvim -u /nix/store/<replaceable>hash</replaceable>-vimrc</command> + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.neovim-unwrapped; + defaultText = literalExpression "pkgs.neovim-unwrapped"; + description = "The package to use for the neovim binary."; + }; + + finalPackage = mkOption { + type = types.package; + visible = false; + readOnly = true; + description = "Resulting customized neovim package."; + }; + + runtime = mkOption { + default = {}; + example = literalExpression '' + { "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; } + ''; + description = '' + Set of files that have to be linked in <filename>runtime</filename>. + ''; + + type = with types; attrsOf (submodule ( + { name, config, ... }: + { options = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether this /etc file should be generated. This + option allows specific /etc files to be disabled. + ''; + }; + + target = mkOption { + type = types.str; + description = '' + Name of symlink. Defaults to the attribute + name. + ''; + }; + + text = mkOption { + default = null; + type = types.nullOr types.lines; + description = "Text of the file."; + }; + + source = mkOption { + type = types.path; + description = "Path of the source file."; + }; + + }; + + config = { + target = mkDefault name; + source = mkIf (config.text != null) ( + let name' = "neovim-runtime" + baseNameOf name; + in mkDefault (pkgs.writeText name' config.text)); + }; + + })); + + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ + cfg.finalPackage + ]; + environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "nvim"); + + programs.neovim.finalPackage = pkgs.wrapNeovim cfg.package { + inherit (cfg) viAlias vimAlias withPython3 withNodeJs withRuby; + configure = cfg.configure // { + + customRC = (cfg.configure.customRC or "") + '' + set runtimepath^=${runtime}/etc + ''; + }; + }; + }; +} diff --git a/nixos/modules/programs/nm-applet.nix b/nixos/modules/programs/nm-applet.nix new file mode 100644 index 00000000000..5bcee30125b --- /dev/null +++ b/nixos/modules/programs/nm-applet.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +{ + meta = { + maintainers = lib.teams.freedesktop.members; + }; + + options.programs.nm-applet = { + enable = lib.mkEnableOption "nm-applet"; + + indicator = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to use indicator instead of status icon. + It is needed for Appindicator environments, like Enlightenment. + ''; + }; + }; + + config = lib.mkIf config.programs.nm-applet.enable { + systemd.user.services.nm-applet = { + description = "Network manager applet"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet ${lib.optionalString config.programs.nm-applet.indicator "--indicator"}"; + }; + + services.dbus.packages = [ pkgs.gcr ]; + }; +} diff --git a/nixos/modules/programs/noisetorch.nix b/nixos/modules/programs/noisetorch.nix new file mode 100644 index 00000000000..f76555289f1 --- /dev/null +++ b/nixos/modules/programs/noisetorch.nix @@ -0,0 +1,28 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let cfg = config.programs.noisetorch; +in { + options.programs.noisetorch = { + enable = mkEnableOption "noisetorch + setcap wrapper"; + + package = mkOption { + type = types.package; + default = pkgs.noisetorch; + defaultText = literalExpression "pkgs.noisetorch"; + description = '' + The noisetorch package to use. + ''; + }; + }; + + config = mkIf cfg.enable { + security.wrappers.noisetorch = { + owner = "root"; + group = "root"; + capabilities = "cap_sys_resource=+ep"; + source = "${cfg.package}/bin/noisetorch"; + }; + }; +} diff --git a/nixos/modules/programs/npm.nix b/nixos/modules/programs/npm.nix new file mode 100644 index 00000000000..d79c6c73400 --- /dev/null +++ b/nixos/modules/programs/npm.nix @@ -0,0 +1,54 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.npm; +in + +{ + ###### interface + + options = { + programs.npm = { + enable = mkEnableOption "<command>npm</command> global config"; + + package = mkOption { + type = types.package; + description = "The npm package version / flavor to use"; + default = pkgs.nodePackages.npm; + defaultText = literalExpression "pkgs.nodePackages.npm"; + example = literalExpression "pkgs.nodePackages_13_x.npm"; + }; + + npmrc = mkOption { + type = lib.types.lines; + description = '' + The system-wide npm configuration. + See <link xlink:href="https://docs.npmjs.com/misc/config"/>. + ''; + default = '' + prefix = ''${HOME}/.npm + ''; + example = '' + prefix = ''${HOME}/.npm + https-proxy=proxy.example.com + init-license=MIT + init-author-url=http://npmjs.org + color=true + ''; + }; + }; + }; + + ###### implementation + + config = lib.mkIf cfg.enable { + environment.etc.npmrc.text = cfg.npmrc; + + environment.variables.NPM_CONFIG_GLOBALCONFIG = "/etc/npmrc"; + + environment.systemPackages = [ cfg.package ]; + }; + +} diff --git a/nixos/modules/programs/oblogout.nix b/nixos/modules/programs/oblogout.nix new file mode 100644 index 00000000000..a039b0623b5 --- /dev/null +++ b/nixos/modules/programs/oblogout.nix @@ -0,0 +1,11 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + imports = [ + (mkRemovedOptionModule [ "programs" "oblogout" ] "programs.oblogout has been removed from NixOS. This is because the oblogout repository has been archived upstream.") + ]; + +} diff --git a/nixos/modules/programs/pantheon-tweaks.nix b/nixos/modules/programs/pantheon-tweaks.nix new file mode 100644 index 00000000000..0b8a19ea22c --- /dev/null +++ b/nixos/modules/programs/pantheon-tweaks.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta = { + maintainers = teams.pantheon.members; + }; + + ###### interface + options = { + programs.pantheon-tweaks.enable = mkEnableOption "Pantheon Tweaks, an unofficial system settings panel for Pantheon"; + }; + + ###### implementation + config = mkIf config.programs.pantheon-tweaks.enable { + services.xserver.desktopManager.pantheon.extraSwitchboardPlugs = [ pkgs.pantheon-tweaks ]; + }; +} diff --git a/nixos/modules/programs/partition-manager.nix b/nixos/modules/programs/partition-manager.nix new file mode 100644 index 00000000000..1be2f0a69a1 --- /dev/null +++ b/nixos/modules/programs/partition-manager.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta.maintainers = [ maintainers.oxalica ]; + + ###### interface + options = { + programs.partition-manager.enable = mkEnableOption "KDE Partition Manager"; + }; + + ###### implementation + config = mkIf config.programs.partition-manager.enable { + services.dbus.packages = [ pkgs.libsForQt5.kpmcore ]; + # `kpmcore` need to be installed to pull in polkit actions. + environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.partition-manager ]; + }; +} diff --git a/nixos/modules/programs/phosh.nix b/nixos/modules/programs/phosh.nix new file mode 100644 index 00000000000..ad875616ac9 --- /dev/null +++ b/nixos/modules/programs/phosh.nix @@ -0,0 +1,162 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.phosh; + + # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop + oskItem = pkgs.makeDesktopItem { + name = "sm.puri.OSK0"; + desktopName = "On-screen keyboard"; + exec = "${pkgs.squeekboard}/bin/squeekboard"; + categories = [ "GNOME" "Core" ]; + onlyShowIn = [ "GNOME" ]; + noDisplay = true; + extraConfig = { + X-GNOME-Autostart-Phase = "Panel"; + X-GNOME-Provides = "inputmethod"; + X-GNOME-Autostart-Notify = "true"; + X-GNOME-AutoRestart = "true"; + }; + }; + + phocConfigType = types.submodule { + options = { + xwayland = mkOption { + description = '' + Whether to enable XWayland support. + + To start XWayland immediately, use `immediate`. + ''; + type = types.enum [ "true" "false" "immediate" ]; + default = "false"; + }; + cursorTheme = mkOption { + description = '' + Cursor theme to use in Phosh. + ''; + type = types.str; + default = "default"; + }; + outputs = mkOption { + description = '' + Output configurations. + ''; + type = types.attrsOf phocOutputType; + default = { + DSI-1 = { + scale = 2; + }; + }; + }; + }; + }; + + phocOutputType = types.submodule { + options = { + modeline = mkOption { + description = '' + One or more modelines. + ''; + type = types.either types.str (types.listOf types.str); + default = []; + example = [ + "87.25 720 776 848 976 1440 1443 1453 1493 -hsync +vsync" + "65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync" + ]; + }; + mode = mkOption { + description = '' + Default video mode. + ''; + type = types.nullOr types.str; + default = null; + example = "768x1024"; + }; + scale = mkOption { + description = '' + Display scaling factor. + ''; + type = types.nullOr types.ints.unsigned; + default = null; + example = 2; + }; + rotate = mkOption { + description = '' + Screen transformation. + ''; + type = types.enum [ + "90" "180" "270" "flipped" "flipped-90" "flipped-180" "flipped-270" null + ]; + default = null; + }; + }; + }; + + optionalKV = k: v: if v == null then "" else "${k} = ${builtins.toString v}"; + + renderPhocOutput = name: output: let + modelines = if builtins.isList output.modeline + then output.modeline + else [ output.modeline ]; + renderModeline = l: "modeline = ${l}"; + in '' + [output:${name}] + ${concatStringsSep "\n" (map renderModeline modelines)} + ${optionalKV "mode" output.mode} + ${optionalKV "scale" output.scale} + ${optionalKV "rotate" output.rotate} + ''; + + renderPhocConfig = phoc: let + outputs = mapAttrsToList renderPhocOutput phoc.outputs; + in '' + [core] + xwayland = ${phoc.xwayland} + ${concatStringsSep "\n" outputs} + [cursor] + theme = ${phoc.cursorTheme} + ''; +in { + options = { + programs.phosh = { + enable = mkEnableOption '' + Whether to enable, Phosh, related packages and default configurations. + ''; + phocConfig = mkOption { + description = '' + Configurations for the Phoc compositor. + ''; + type = types.oneOf [ types.lines types.path phocConfigType ]; + default = {}; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ + pkgs.phoc + pkgs.phosh + pkgs.squeekboard + oskItem + ]; + + systemd.packages = [ pkgs.phosh ]; + + programs.feedbackd.enable = true; + + security.pam.services.phosh = {}; + + hardware.opengl.enable = mkDefault true; + + services.gnome.core-shell.enable = true; + services.gnome.core-os-services.enable = true; + services.xserver.displayManager.sessionPackages = [ pkgs.phosh ]; + + environment.etc."phosh/phoc.ini".source = + if builtins.isPath cfg.phocConfig then cfg.phocConfig + else if builtins.isString cfg.phocConfig then pkgs.writeText "phoc.ini" cfg.phocConfig + else pkgs.writeText "phoc.ini" (renderPhocConfig cfg.phocConfig); + }; +} diff --git a/nixos/modules/programs/plotinus.nix b/nixos/modules/programs/plotinus.nix new file mode 100644 index 00000000000..2c90a41ba02 --- /dev/null +++ b/nixos/modules/programs/plotinus.nix @@ -0,0 +1,36 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.plotinus; +in +{ + meta = { + maintainers = pkgs.plotinus.meta.maintainers; + doc = ./plotinus.xml; + }; + + ###### interface + + options = { + programs.plotinus = { + enable = mkOption { + default = false; + description = '' + Whether to enable the Plotinus GTK 3 plugin. Plotinus provides a + popup (triggered by Ctrl-Shift-P) to search the menus of a + compatible application. + ''; + type = types.bool; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + environment.sessionVariables.XDG_DATA_DIRS = [ "${pkgs.plotinus}/share/gsettings-schemas/${pkgs.plotinus.name}" ]; + environment.variables.GTK3_MODULES = [ "${pkgs.plotinus}/lib/libplotinus.so" ]; + }; +} diff --git a/nixos/modules/programs/plotinus.xml b/nixos/modules/programs/plotinus.xml new file mode 100644 index 00000000000..8fc8c22c6d7 --- /dev/null +++ b/nixos/modules/programs/plotinus.xml @@ -0,0 +1,30 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-program-plotinus"> + <title>Plotinus</title> + <para> + <emphasis>Source:</emphasis> + <filename>modules/programs/plotinus.nix</filename> + </para> + <para> + <emphasis>Upstream documentation:</emphasis> + <link xlink:href="https://github.com/p-e-w/plotinus"/> + </para> + <para> + Plotinus is a searchable command palette in every modern GTK application. + </para> + <para> + When in a GTK 3 application and Plotinus is enabled, you can press + <literal>Ctrl+Shift+P</literal> to open the command palette. The command + palette provides a searchable list of of all menu items in the application. + </para> + <para> + To enable Plotinus, add the following to your + <filename>configuration.nix</filename>: +<programlisting> +<xref linkend="opt-programs.plotinus.enable"/> = true; +</programlisting> + </para> +</chapter> diff --git a/nixos/modules/programs/proxychains.nix b/nixos/modules/programs/proxychains.nix new file mode 100644 index 00000000000..3f44e23a93e --- /dev/null +++ b/nixos/modules/programs/proxychains.nix @@ -0,0 +1,165 @@ +{ config, lib, pkgs, ... }: +with lib; +let + + cfg = config.programs.proxychains; + + configFile = '' + ${cfg.chain.type}_chain + ${optionalString (cfg.chain.type == "random") + "chain_len = ${builtins.toString cfg.chain.length}"} + ${optionalString cfg.proxyDNS "proxy_dns"} + ${optionalString cfg.quietMode "quiet_mode"} + remote_dns_subnet ${builtins.toString cfg.remoteDNSSubnet} + tcp_read_time_out ${builtins.toString cfg.tcpReadTimeOut} + tcp_connect_time_out ${builtins.toString cfg.tcpConnectTimeOut} + localnet ${cfg.localnet} + [ProxyList] + ${builtins.concatStringsSep "\n" + (lib.mapAttrsToList (k: v: "${v.type} ${v.host} ${builtins.toString v.port}") + (lib.filterAttrs (k: v: v.enable) cfg.proxies))} + ''; + + proxyOptions = { + options = { + enable = mkEnableOption "this proxy"; + + type = mkOption { + type = types.enum [ "http" "socks4" "socks5" ]; + description = "Proxy type."; + }; + + host = mkOption { + type = types.str; + description = "Proxy host or IP address."; + }; + + port = mkOption { + type = types.port; + description = "Proxy port"; + }; + }; + }; + +in { + + ###### interface + + options = { + + programs.proxychains = { + + enable = mkEnableOption "installing proxychains configuration"; + + chain = { + type = mkOption { + type = types.enum [ "dynamic" "strict" "random" ]; + default = "strict"; + description = '' + <literal>dynamic</literal> - Each connection will be done via chained proxies + all proxies chained in the order as they appear in the list + at least one proxy must be online to play in chain + (dead proxies are skipped) + otherwise <literal>EINTR</literal> is returned to the app. + + <literal>strict</literal> - Each connection will be done via chained proxies + all proxies chained in the order as they appear in the list + all proxies must be online to play in chain + otherwise <literal>EINTR</literal> is returned to the app. + + <literal>random</literal> - Each connection will be done via random proxy + (or proxy chain, see <option>programs.proxychains.chain.length</option>) from the list. + ''; + }; + length = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Chain length for random chain. + ''; + }; + }; + + proxyDNS = mkOption { + type = types.bool; + default = true; + description = "Proxy DNS requests - no leak for DNS data."; + }; + + quietMode = mkEnableOption "Quiet mode (no output from the library)."; + + remoteDNSSubnet = mkOption { + type = types.enum [ 10 127 224 ]; + default = 224; + description = '' + Set the class A subnet number to use for the internal remote DNS mapping, uses the reserved 224.x.x.x range by default. + ''; + }; + + tcpReadTimeOut = mkOption { + type = types.int; + default = 15000; + description = "Connection read time-out in milliseconds."; + }; + + tcpConnectTimeOut = mkOption { + type = types.int; + default = 8000; + description = "Connection time-out in milliseconds."; + }; + + localnet = mkOption { + type = types.str; + default = "127.0.0.0/255.0.0.0"; + description = "By default enable localnet for loopback address ranges."; + }; + + proxies = mkOption { + type = types.attrsOf (types.submodule proxyOptions); + description = '' + Proxies to be used by proxychains. + ''; + + example = literalExpression '' + { myproxy = + { type = "socks4"; + host = "127.0.0.1"; + port = 1337; + }; + } + ''; + }; + + }; + + }; + + ###### implementation + + meta.maintainers = with maintainers; [ sorki ]; + + config = mkIf cfg.enable { + + assertions = singleton { + assertion = cfg.chain.type != "random" && cfg.chain.length == null; + message = '' + Option `programs.proxychains.chain.length` + only makes sense with `programs.proxychains.chain.type` = "random". + ''; + }; + + programs.proxychains.proxies = mkIf config.services.tor.client.enable + { + torproxy = mkDefault { + enable = true; + type = "socks4"; + host = "127.0.0.1"; + port = 9050; + }; + }; + + environment.etc."proxychains.conf".text = configFile; + environment.systemPackages = [ pkgs.proxychains ]; + }; + +} diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix new file mode 100644 index 00000000000..88e861bf403 --- /dev/null +++ b/nixos/modules/programs/qt5ct.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta.maintainers = [ maintainers.romildo ]; + + ###### interface + options = { + programs.qt5ct = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the Qt5 Configuration Tool (qt5ct), a + program that allows users to configure Qt5 settings (theme, + font, icons, etc.) under desktop environments or window + manager without Qt integration. + + Official home page: <link xlink:href="https://sourceforge.net/projects/qt5ct/">https://sourceforge.net/projects/qt5ct/</link> + ''; + }; + }; + }; + + ###### implementation + config = mkIf config.programs.qt5ct.enable { + environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct"; + environment.systemPackages = with pkgs; [ libsForQt5.qt5ct ]; + }; +} diff --git a/nixos/modules/programs/screen.nix b/nixos/modules/programs/screen.nix new file mode 100644 index 00000000000..728a0eb8cea --- /dev/null +++ b/nixos/modules/programs/screen.nix @@ -0,0 +1,33 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) mkOption mkIf types; + cfg = config.programs.screen; +in + +{ + ###### interface + + options = { + programs.screen = { + + screenrc = mkOption { + default = ""; + description = '' + The contents of /etc/screenrc file. + ''; + type = types.lines; + }; + }; + }; + + ###### implementation + + config = mkIf (cfg.screenrc != "") { + environment.etc.screenrc.text = cfg.screenrc; + + environment.systemPackages = [ pkgs.screen ]; + security.pam.services.screen = {}; + }; + +} diff --git a/nixos/modules/programs/seahorse.nix b/nixos/modules/programs/seahorse.nix new file mode 100644 index 00000000000..c0a356bff57 --- /dev/null +++ b/nixos/modules/programs/seahorse.nix @@ -0,0 +1,46 @@ +# Seahorse. + +{ config, pkgs, lib, ... }: + +with lib; + +{ + + # Added 2019-08-27 + imports = [ + (mkRenamedOptionModule + [ "services" "gnome3" "seahorse" "enable" ] + [ "programs" "seahorse" "enable" ]) + ]; + + + ###### interface + + options = { + + programs.seahorse = { + + enable = mkEnableOption "Seahorse, a GNOME application for managing encryption keys and passwords in the GNOME Keyring"; + + }; + + }; + + + ###### implementation + + config = mkIf config.programs.seahorse.enable { + + programs.ssh.askPassword = mkDefault "${pkgs.gnome.seahorse}/libexec/seahorse/ssh-askpass"; + + environment.systemPackages = [ + pkgs.gnome.seahorse + ]; + + services.dbus.packages = [ + pkgs.gnome.seahorse + ]; + + }; + +} diff --git a/nixos/modules/programs/sedutil.nix b/nixos/modules/programs/sedutil.nix new file mode 100644 index 00000000000..7efc80f4abb --- /dev/null +++ b/nixos/modules/programs/sedutil.nix @@ -0,0 +1,18 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.sedutil; + +in { + options.programs.sedutil.enable = mkEnableOption "sedutil"; + + config = mkIf cfg.enable { + boot.kernelParams = [ + "libata.allow_tpm=1" + ]; + + environment.systemPackages = with pkgs; [ sedutil ]; + }; +} diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix new file mode 100644 index 00000000000..963cd8853db --- /dev/null +++ b/nixos/modules/programs/shadow.nix @@ -0,0 +1,129 @@ +# Configuration for the pwdutils suite of tools: passwd, useradd, etc. + +{ config, lib, utils, pkgs, ... }: + +with lib; + +let + + /* + There are three different sources for user/group id ranges, each of which gets + used by different programs: + - The login.defs file, used by the useradd, groupadd and newusers commands + - The update-users-groups.pl file, used by NixOS in the activation phase to + decide on which ids to use for declaratively defined users without a static + id + - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used + by systemd for features like ConditionUser=@system and systemd-sysusers + */ + loginDefs = + '' + DEFAULT_HOME yes + + SYS_UID_MIN 400 + SYS_UID_MAX 999 + UID_MIN 1000 + UID_MAX 29999 + + SYS_GID_MIN 400 + SYS_GID_MAX 999 + GID_MIN 1000 + GID_MAX 29999 + + TTYGROUP tty + TTYPERM 0620 + + # Ensure privacy for newly created home directories. + UMASK 077 + + # Uncomment this and install chfn SUID to allow non-root + # users to change their account GECOS information. + # This should be made configurable. + #CHFN_RESTRICT frwh + + ''; + + mkSetuidRoot = source: + { setuid = true; + owner = "root"; + group = "root"; + inherit source; + }; + +in + +{ + + ###### interface + + options = { + + users.defaultUserShell = lib.mkOption { + description = '' + This option defines the default shell assigned to user + accounts. This can be either a full system path or a shell package. + + This must not be a store path, since the path is + used outside the store (in particular in /etc/passwd). + ''; + example = literalExpression "pkgs.zsh"; + type = types.either types.path types.shellPackage; + }; + + }; + + + ###### implementation + + config = { + + environment.systemPackages = + lib.optional config.users.mutableUsers pkgs.shadow ++ + lib.optional (types.shellPackage.check config.users.defaultUserShell) + config.users.defaultUserShell; + + environment.etc = + { # /etc/login.defs: global configuration for pwdutils. You + # cannot login without it! + "login.defs".source = pkgs.writeText "login.defs" loginDefs; + + # /etc/default/useradd: configuration for useradd. + "default/useradd".source = pkgs.writeText "useradd" + '' + GROUP=100 + HOME=/home + SHELL=${utils.toShellPath config.users.defaultUserShell} + ''; + }; + + security.pam.services = + { chsh = { rootOK = true; }; + chfn = { rootOK = true; }; + su = { rootOK = true; forwardXAuth = true; logFailures = true; }; + passwd = {}; + # Note: useradd, groupadd etc. aren't setuid root, so it + # doesn't really matter what the PAM config says as long as it + # lets root in. + useradd = { rootOK = true; }; + usermod = { rootOK = true; }; + userdel = { rootOK = true; }; + groupadd = { rootOK = true; }; + groupmod = { rootOK = true; }; + groupmems = { rootOK = true; }; + groupdel = { rootOK = true; }; + login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; }; + chpasswd = { rootOK = true; }; + }; + + security.wrappers = { + su = mkSetuidRoot "${pkgs.shadow.su}/bin/su"; + sg = mkSetuidRoot "${pkgs.shadow.out}/bin/sg"; + newgrp = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp"; + newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap"; + newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap"; + } // lib.optionalAttrs config.users.mutableUsers { + chsh = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh"; + passwd = mkSetuidRoot "${pkgs.shadow.out}/bin/passwd"; + }; + }; +} diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix new file mode 100644 index 00000000000..db935abe4bb --- /dev/null +++ b/nixos/modules/programs/singularity.nix @@ -0,0 +1,34 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.programs.singularity; + singularity = pkgs.singularity.overrideAttrs (attrs : { + installPhase = attrs.installPhase + '' + mv $out/libexec/singularity/bin/starter-suid $out/libexec/singularity/bin/starter-suid.orig + ln -s /run/wrappers/bin/singularity-suid $out/libexec/singularity/bin/starter-suid + ''; + }); +in { + options.programs.singularity = { + enable = mkEnableOption "Singularity"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ singularity ]; + security.wrappers.singularity-suid = + { setuid = true; + owner = "root"; + group = "root"; + source = "${singularity}/libexec/singularity/bin/starter-suid.orig"; + }; + systemd.tmpfiles.rules = [ + "d /var/singularity/mnt/session 0770 root root -" + "d /var/singularity/mnt/final 0770 root root -" + "d /var/singularity/mnt/overlay 0770 root root -" + "d /var/singularity/mnt/container 0770 root root -" + "d /var/singularity/mnt/source 0770 root root -" + ]; + }; + +} diff --git a/nixos/modules/programs/slock.nix b/nixos/modules/programs/slock.nix new file mode 100644 index 00000000000..ce80fcc5d4a --- /dev/null +++ b/nixos/modules/programs/slock.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.slock; + +in +{ + options = { + programs.slock = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to install slock screen locker with setuid wrapper. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.slock ]; + security.wrappers.slock = + { setuid = true; + owner = "root"; + group = "root"; + source = "${pkgs.slock.out}/bin/slock"; + }; + }; +} diff --git a/nixos/modules/programs/spacefm.nix b/nixos/modules/programs/spacefm.nix new file mode 100644 index 00000000000..f71abcaa332 --- /dev/null +++ b/nixos/modules/programs/spacefm.nix @@ -0,0 +1,55 @@ +# Global configuration for spacefm. + +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.programs.spacefm; + +in +{ + ###### interface + + options = { + + programs.spacefm = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to install SpaceFM and create <filename>/etc/spacefm/spacefm.conf</filename>. + ''; + }; + + settings = mkOption { + type = types.attrs; + default = { + tmp_dir = "/tmp"; + terminal_su = "${pkgs.sudo}/bin/sudo"; + }; + defaultText = literalExpression '' + { + tmp_dir = "/tmp"; + terminal_su = "''${pkgs.sudo}/bin/sudo"; + } + ''; + description = '' + The system-wide spacefm configuration. + Parameters to be written to <filename>/etc/spacefm/spacefm.conf</filename>. + Refer to the <link xlink:href="https://ignorantguru.github.io/spacefm/spacefm-manual-en.html#programfiles-etc">relevant entry</link> in the SpaceFM manual. + ''; + }; + + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.spaceFM ]; + + environment.etc."spacefm/spacefm.conf".text = + concatStrings (mapAttrsToList (n: v: "${n}=${toString v}\n") cfg.settings); + }; +} diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix new file mode 100644 index 00000000000..b31fce91524 --- /dev/null +++ b/nixos/modules/programs/ssh.nix @@ -0,0 +1,346 @@ +# Global configuration for the SSH client. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.ssh; + + askPassword = cfg.askPassword; + + askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper" + '' + #! ${pkgs.runtimeShell} -e + export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')" + exec ${askPassword} "$@" + ''; + + knownHosts = attrValues cfg.knownHosts; + + knownHostsText = (flip (concatMapStringsSep "\n") knownHosts + (h: assert h.hostNames != []; + optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " " + + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile) + )) + "\n"; + + knownHostsFiles = [ "/etc/ssh/ssh_known_hosts" "/etc/ssh/ssh_known_hosts2" ] + ++ map pkgs.copyPathToStore cfg.knownHostsFiles; + +in +{ + ###### interface + + options = { + + programs.ssh = { + + enableAskPassword = mkOption { + type = types.bool; + default = config.services.xserver.enable; + defaultText = literalExpression "config.services.xserver.enable"; + description = "Whether to configure SSH_ASKPASS in the environment."; + }; + + askPassword = mkOption { + type = types.str; + default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"; + defaultText = literalExpression ''"''${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"''; + description = "Program used by SSH to ask for passwords."; + }; + + forwardX11 = mkOption { + type = types.bool; + default = false; + description = '' + Whether to request X11 forwarding on outgoing connections by default. + This is useful for running graphical programs on the remote machine and have them display to your local X11 server. + Historically, this value has depended on the value used by the local sshd daemon, but there really isn't a relation between the two. + Note: there are some security risks to forwarding an X11 connection. + NixOS's X server is built with the SECURITY extension, which prevents some obvious attacks. + To enable or disable forwarding on a per-connection basis, see the -X and -x options to ssh. + The -Y option to ssh enables trusted forwarding, which bypasses the SECURITY extension. + ''; + }; + + setXAuthLocation = mkOption { + type = types.bool; + description = '' + Whether to set the path to <command>xauth</command> for X11-forwarded connections. + This causes a dependency on X11 packages. + ''; + }; + + pubkeyAcceptedKeyTypes = mkOption { + type = types.listOf types.str; + default = []; + example = [ "ssh-ed25519" "ssh-rsa" ]; + description = '' + Specifies the key types that will be used for public key authentication. + ''; + }; + + hostKeyAlgorithms = mkOption { + type = types.listOf types.str; + default = []; + example = [ "ssh-ed25519" "ssh-rsa" ]; + description = '' + Specifies the host key algorithms that the client wants to use in order of preference. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration text prepended to <filename>ssh_config</filename>. Other generated + options will be added after a <code>Host *</code> pattern. + See <citerefentry><refentrytitle>ssh_config</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for help. + ''; + }; + + startAgent = mkOption { + type = types.bool; + default = false; + description = '' + Whether to start the OpenSSH agent when you log in. The OpenSSH agent + remembers private keys for you so that you don't have to type in + passphrases every time you make an SSH connection. Use + <command>ssh-add</command> to add a key to the agent. + ''; + }; + + agentTimeout = mkOption { + type = types.nullOr types.str; + default = null; + example = "1h"; + description = '' + How long to keep the private keys in memory. Use null to keep them forever. + ''; + }; + + agentPKCS11Whitelist = mkOption { + type = types.nullOr types.str; + default = null; + example = literalExpression ''"''${pkgs.opensc}/lib/opensc-pkcs11.so"''; + description = '' + A pattern-list of acceptable paths for PKCS#11 shared libraries + that may be used with the -s option to ssh-add. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.openssh; + defaultText = literalExpression "pkgs.openssh"; + description = '' + The package used for the openssh client and daemon. + ''; + }; + + knownHosts = mkOption { + default = {}; + type = types.attrsOf (types.submodule ({ name, config, options, ... }: { + options = { + certAuthority = mkOption { + type = types.bool; + default = false; + description = '' + This public key is an SSH certificate authority, rather than an + individual host's key. + ''; + }; + hostNames = mkOption { + type = types.listOf types.str; + default = [ name ] ++ config.extraHostNames; + defaultText = literalExpression "[ ${name} ] ++ config.${options.extraHostNames}"; + description = '' + DEPRECATED, please use <literal>extraHostNames</literal>. + A list of host names and/or IP numbers used for accessing + the host's ssh service. + ''; + }; + extraHostNames = mkOption { + type = types.listOf types.str; + default = []; + description = '' + A list of additional host names and/or IP numbers used for + accessing the host's ssh service. + ''; + }; + publicKey = mkOption { + default = null; + type = types.nullOr types.str; + example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg=="; + description = '' + The public key data for the host. You can fetch a public key + from a running SSH server with the <command>ssh-keyscan</command> + command. The public key should not include any host names, only + the key type and the key itself. + ''; + }; + publicKeyFile = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + The path to the public key file for the host. The public + key file is read at build time and saved in the Nix store. + You can fetch a public key file from a running SSH server + with the <command>ssh-keyscan</command> command. The content + of the file should follow the same format as described for + the <literal>publicKey</literal> option. Only a single key + is supported. If a host has multiple keys, use + <option>programs.ssh.knownHostsFiles</option> instead. + ''; + }; + }; + })); + description = '' + The set of system-wide known SSH hosts. + ''; + example = literalExpression '' + { + myhost = { + extraHostNames = [ "myhost.mydomain.com" "10.10.1.4" ]; + publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub; + }; + "myhost2.net".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIRuJ8p1Fi+m6WkHV0KWnRfpM1WxoW8XAS+XvsSKsTK"; + } + ''; + }; + + knownHostsFiles = mkOption { + default = []; + type = with types; listOf path; + description = '' + Files containing SSH host keys to set as global known hosts. + <literal>/etc/ssh/ssh_known_hosts</literal> (which is + generated by <option>programs.ssh.knownHosts</option>) and + <literal>/etc/ssh/ssh_known_hosts2</literal> are always + included. + ''; + example = literalExpression '' + [ + ./known_hosts + (writeText "github.keys" ''' + github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + ''') + ] + ''; + }; + + kexAlgorithms = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "curve25519-sha256@libssh.org" "diffie-hellman-group-exchange-sha256" ]; + description = '' + Specifies the available KEX (Key Exchange) algorithms. + ''; + }; + + ciphers = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "chacha20-poly1305@openssh.com" "aes256-gcm@openssh.com" ]; + description = '' + Specifies the ciphers allowed and their order of preference. + ''; + }; + + macs = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha1" ]; + description = '' + Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used + for data integrity protection. + ''; + }; + }; + + }; + + config = { + + programs.ssh.setXAuthLocation = + mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 || config.services.openssh.forwardX11); + + assertions = + [ { assertion = cfg.forwardX11 -> cfg.setXAuthLocation; + message = "cannot enable X11 forwarding without setting XAuth location"; + } + ] ++ flip mapAttrsToList cfg.knownHosts (name: data: { + assertion = (data.publicKey == null && data.publicKeyFile != null) || + (data.publicKey != null && data.publicKeyFile == null); + message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; + }); + + warnings = mapAttrsToList (name: _: ''programs.ssh.knownHosts.${name}.hostNames is deprecated, use programs.ssh.knownHosts.${name}.extraHostNames'') + (filterAttrs (name: {hostNames, extraHostNames, ...}: hostNames != [ name ] ++ extraHostNames) cfg.knownHosts); + + # SSH configuration. Slight duplication of the sshd_config + # generation in the sshd service. + environment.etc."ssh/ssh_config".text = + '' + # Custom options from `extraConfig`, to override generated options + ${cfg.extraConfig} + + # Generated options from other settings + Host * + AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"} + GlobalKnownHostsFile ${concatStringsSep " " knownHostsFiles} + + ${optionalString cfg.setXAuthLocation '' + XAuthLocation ${pkgs.xorg.xauth}/bin/xauth + ''} + + ForwardX11 ${if cfg.forwardX11 then "yes" else "no"} + + ${optionalString (cfg.pubkeyAcceptedKeyTypes != []) "PubkeyAcceptedKeyTypes ${concatStringsSep "," cfg.pubkeyAcceptedKeyTypes}"} + ${optionalString (cfg.hostKeyAlgorithms != []) "HostKeyAlgorithms ${concatStringsSep "," cfg.hostKeyAlgorithms}"} + ${optionalString (cfg.kexAlgorithms != null) "KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}"} + ${optionalString (cfg.ciphers != null) "Ciphers ${concatStringsSep "," cfg.ciphers}"} + ${optionalString (cfg.macs != null) "MACs ${concatStringsSep "," cfg.macs}"} + ''; + + environment.etc."ssh/ssh_known_hosts".text = knownHostsText; + + # FIXME: this should really be socket-activated for über-awesomeness. + systemd.user.services.ssh-agent = mkIf cfg.startAgent + { description = "SSH Agent"; + wantedBy = [ "default.target" ]; + unitConfig.ConditionUser = "!@system"; + serviceConfig = + { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent"; + ExecStart = + "${cfg.package}/bin/ssh-agent " + + optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") + + optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ") + + "-a %t/ssh-agent"; + StandardOutput = "null"; + Type = "forking"; + Restart = "on-failure"; + SuccessExitStatus = "0 2"; + }; + # Allow ssh-agent to ask for confirmation. This requires the + # unit to know about the user's $DISPLAY (via ‘systemctl + # import-environment’). + environment.SSH_ASKPASS = optionalString cfg.enableAskPassword askPasswordWrapper; + environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS + }; + + environment.extraInit = optionalString cfg.startAgent + '' + if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then + export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent" + fi + ''; + + environment.variables.SSH_ASKPASS = optionalString cfg.enableAskPassword askPassword; + + }; +} diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix new file mode 100644 index 00000000000..b454bf35229 --- /dev/null +++ b/nixos/modules/programs/ssmtp.nix @@ -0,0 +1,190 @@ +# Configuration for `ssmtp', a trivial mail transfer agent that can +# replace sendmail/postfix on simple systems. It delivers email +# directly to an SMTP server defined in its configuration file, without +# queueing mail locally. + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ssmtp; + +in +{ + + imports = [ + (mkRenamedOptionModule [ "networking" "defaultMailServer" "directDelivery" ] [ "services" "ssmtp" "enable" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "hostName" ] [ "services" "ssmtp" "hostName" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "domain" ] [ "services" "ssmtp" "domain" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "root" ] [ "services" "ssmtp" "root" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "useTLS" ] [ "services" "ssmtp" "useTLS" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "useSTARTTLS" ] [ "services" "ssmtp" "useSTARTTLS" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "authUser" ] [ "services" "ssmtp" "authUser" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPassFile" ] [ "services" "ssmtp" "authPassFile" ]) + (mkRenamedOptionModule [ "networking" "defaultMailServer" "setSendmail" ] [ "services" "ssmtp" "setSendmail" ]) + + (mkRemovedOptionModule [ "networking" "defaultMailServer" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path") + (mkRemovedOptionModule [ "services" "ssmtp" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path") + ]; + + options = { + + services.ssmtp = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Use the trivial Mail Transfer Agent (MTA) + <command>ssmtp</command> package to allow programs to send + e-mail. If you don't want to run a “real” MTA like + <command>sendmail</command> or <command>postfix</command> on + your machine, set this option to <literal>true</literal>, and + set the option + <option>services.ssmtp.hostName</option> to the + host name of your preferred mail server. + ''; + }; + + settings = mkOption { + type = with types; attrsOf (oneOf [ bool str ]); + default = {}; + description = '' + <citerefentry><refentrytitle>ssmtp</refentrytitle><manvolnum>5</manvolnum></citerefentry> configuration. Refer + to <link xlink:href="https://linux.die.net/man/5/ssmtp.conf"/> for details on supported values. + ''; + example = literalExpression '' + { + Debug = true; + FromLineOverride = false; + } + ''; + }; + + hostName = mkOption { + type = types.str; + example = "mail.example.org"; + description = '' + The host name of the default mail server to use to deliver + e-mail. Can also contain a port number (ex: mail.example.org:587), + defaults to port 25 if no port is given. + ''; + }; + + root = mkOption { + type = types.str; + default = ""; + example = "root@example.org"; + description = '' + The e-mail to which mail for users with UID < 1000 is forwarded. + ''; + }; + + domain = mkOption { + type = types.str; + default = ""; + example = "example.org"; + description = '' + The domain from which mail will appear to be sent. + ''; + }; + + useTLS = mkOption { + type = types.bool; + default = false; + description = '' + Whether TLS should be used to connect to the default mail + server. + ''; + }; + + useSTARTTLS = mkOption { + type = types.bool; + default = false; + description = '' + Whether the STARTTLS should be used to connect to the default + mail server. (This is needed for TLS-capable mail servers + running on the default SMTP port 25.) + ''; + }; + + authUser = mkOption { + type = types.str; + default = ""; + example = "foo@example.org"; + description = '' + Username used for SMTP auth. Leave blank to disable. + ''; + }; + + authPassFile = mkOption { + type = types.nullOr types.str; + default = null; + example = "/run/keys/ssmtp-authpass"; + description = '' + Path to a file that contains the password used for SMTP auth. The file + should not contain a trailing newline, if the password does not contain one + (e.g. use <command>echo -n "password" > file</command>). + This file should be readable by the users that need to execute ssmtp. + ''; + }; + + setSendmail = mkOption { + type = types.bool; + default = true; + description = "Whether to set the system sendmail to ssmtp's."; + }; + + }; + + }; + + + config = mkIf cfg.enable { + + assertions = [ + { + assertion = cfg.useSTARTTLS -> cfg.useTLS; + message = "services.ssmtp.useSTARTTLS has no effect without services.ssmtp.useTLS"; + } + ]; + + services.ssmtp.settings = mkMerge [ + ({ + MailHub = cfg.hostName; + FromLineOverride = mkDefault true; + UseTLS = cfg.useTLS; + UseSTARTTLS = cfg.useSTARTTLS; + }) + (mkIf (cfg.root != "") { root = cfg.root; }) + (mkIf (cfg.domain != "") { rewriteDomain = cfg.domain; }) + (mkIf (cfg.authUser != "") { AuthUser = cfg.authUser; }) + (mkIf (cfg.authPassFile != null) { AuthPassFile = cfg.authPassFile; }) + ]; + + # careful here: ssmtp REQUIRES all config lines to end with a newline char! + environment.etc."ssmtp/ssmtp.conf".text = with generators; toKeyValue { + mkKeyValue = mkKeyValueDefault { + mkValueString = value: + if value == true then "YES" + else if value == false then "NO" + else mkValueStringDefault {} value + ; + } "="; + } cfg.settings; + + environment.systemPackages = [pkgs.ssmtp]; + + services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail { + program = "sendmail"; + source = "${pkgs.ssmtp}/bin/sendmail"; + setuid = false; + setgid = false; + owner = "root"; + group = "root"; + }; + + }; + +} diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix new file mode 100644 index 00000000000..83d2272003c --- /dev/null +++ b/nixos/modules/programs/starship.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.starship; + + settingsFormat = pkgs.formats.toml { }; + + settingsFile = settingsFormat.generate "starship.toml" cfg.settings; + +in { + options.programs.starship = { + enable = mkEnableOption "the Starship shell prompt"; + + settings = mkOption { + inherit (settingsFormat) type; + default = { }; + description = '' + Configuration included in <literal>starship.toml</literal>. + + See https://starship.rs/config/#prompt for documentation. + ''; + }; + }; + + config = mkIf cfg.enable { + programs.bash.promptInit = '' + if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then + export STARSHIP_CONFIG=${settingsFile} + eval "$(${pkgs.starship}/bin/starship init bash)" + fi + ''; + + programs.fish.promptInit = '' + if test "$TERM" != "dumb" -a \( -z "$INSIDE_EMACS" -o "$INSIDE_EMACS" = "vterm" \) + set -x STARSHIP_CONFIG ${settingsFile} + eval (${pkgs.starship}/bin/starship init fish) + end + ''; + + programs.zsh.promptInit = '' + if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then + export STARSHIP_CONFIG=${settingsFile} + eval "$(${pkgs.starship}/bin/starship init zsh)" + fi + ''; + }; + + meta.maintainers = pkgs.starship.meta.maintainers; +} diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix new file mode 100644 index 00000000000..ff4deba2bf0 --- /dev/null +++ b/nixos/modules/programs/steam.nix @@ -0,0 +1,63 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.steam; + + steam = pkgs.steam.override { + extraLibraries = pkgs: with config.hardware.opengl; + if pkgs.hostPlatform.is64bit + then [ package ] ++ extraPackages + else [ package32 ] ++ extraPackages32; + }; +in { + options.programs.steam = { + enable = mkEnableOption "steam"; + + remotePlay.openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for Steam Remote Play. + ''; + }; + + dedicatedServer.openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for Source Dedicated Server. + ''; + }; + }; + + config = mkIf cfg.enable { + hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932 + enable = true; + driSupport = true; + driSupport32Bit = true; + }; + + # optionally enable 32bit pulseaudio support if pulseaudio is enabled + hardware.pulseaudio.support32Bit = config.hardware.pulseaudio.enable; + + hardware.steam-hardware.enable = true; + + environment.systemPackages = [ steam steam.run ]; + + networking.firewall = lib.mkMerge [ + (mkIf cfg.remotePlay.openFirewall { + allowedTCPPorts = [ 27036 ]; + allowedUDPPortRanges = [ { from = 27031; to = 27036; } ]; + }) + + (mkIf cfg.dedicatedServer.openFirewall { + allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port + allowedUDPPorts = [ 27015 ]; # Gameplay traffic + }) + ]; + }; + + meta.maintainers = with maintainers; [ mkg20001 ]; +} diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix new file mode 100644 index 00000000000..01b04728134 --- /dev/null +++ b/nixos/modules/programs/sway.nix @@ -0,0 +1,150 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.sway; + + wrapperOptions = types.submodule { + options = + let + mkWrapperFeature = default: description: mkOption { + type = types.bool; + inherit default; + example = !default; + description = "Whether to make use of the ${description}"; + }; + in { + base = mkWrapperFeature true '' + base wrapper to execute extra session commands and prepend a + dbus-run-session to the sway command. + ''; + gtk = mkWrapperFeature false '' + wrapGAppsHook wrapper to execute sway with required environment + variables for GTK applications. + ''; + }; + }; + + swayPackage = pkgs.sway.override { + extraSessionCommands = cfg.extraSessionCommands; + extraOptions = cfg.extraOptions; + withBaseWrapper = cfg.wrapperFeatures.base; + withGtkWrapper = cfg.wrapperFeatures.gtk; + isNixOS = true; + }; +in { + options.programs.sway = { + enable = mkEnableOption '' + Sway, the i3-compatible tiling Wayland compositor. You can manually launch + Sway by executing "exec sway" on a TTY. Copy /etc/sway/config to + ~/.config/sway/config to modify the default configuration. See + <link xlink:href="https://github.com/swaywm/sway/wiki" /> and + "man 5 sway" for more information''; + + wrapperFeatures = mkOption { + type = wrapperOptions; + default = { }; + example = { gtk = true; }; + description = '' + Attribute set of features to enable in the wrapper. + ''; + }; + + extraSessionCommands = mkOption { + type = types.lines; + default = ""; + example = '' + # SDL: + export SDL_VIDEODRIVER=wayland + # QT (needs qt5.qtwayland in systemPackages): + export QT_QPA_PLATFORM=wayland-egl + export QT_WAYLAND_DISABLE_WINDOWDECORATION="1" + # Fix for some Java AWT applications (e.g. Android Studio), + # use this if they aren't displayed properly: + export _JAVA_AWT_WM_NONREPARENTING=1 + ''; + description = '' + Shell commands executed just before Sway is started. See + <link xlink:href="https://github.com/swaywm/sway/wiki/Running-programs-natively-under-wayland" /> + and <link xlink:href="https://github.com/swaywm/wlroots/blob/master/docs/env_vars.md" /> + for some useful environment variables. + ''; + }; + + extraOptions = mkOption { + type = types.listOf types.str; + default = []; + example = [ + "--verbose" + "--debug" + "--unsupported-gpu" + "--my-next-gpu-wont-be-nvidia" + ]; + description = '' + Command line arguments passed to launch Sway. Please DO NOT report + issues if you use an unsupported GPU (proprietary drivers). + ''; + }; + + extraPackages = mkOption { + type = with types; listOf package; + default = with pkgs; [ + swaylock swayidle foot dmenu + ]; + defaultText = literalExpression '' + with pkgs; [ swaylock swayidle foot dmenu ]; + ''; + example = literalExpression '' + with pkgs; [ + i3status i3status-rust + termite rofi light + ] + ''; + description = '' + Extra packages to be installed system wide. See + <link xlink:href="https://github.com/swaywm/sway/wiki/Useful-add-ons-for-sway" /> and + <link xlink:href="https://github.com/swaywm/sway/wiki/i3-Migration-Guide#common-x11-apps-used-on-i3-with-wayland-alternatives" /> + for a list of useful software. + ''; + }; + + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base; + message = '' + The extraSessionCommands for Sway will not be run if + wrapperFeatures.base is disabled. + ''; + } + ]; + environment = { + systemPackages = [ swayPackage ] ++ cfg.extraPackages; + # Needed for the default wallpaper: + pathsToLink = [ "/share/backgrounds/sway" ]; + etc = { + "sway/config".source = mkOptionDefault "${swayPackage}/etc/sway/config"; + "sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" '' + # Import the most important environment variables into the D-Bus and systemd + # user environments (e.g. required for screen sharing and Pinentry prompts): + exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP + ''; + }; + }; + security.polkit.enable = true; + security.pam.services.swaylock = {}; + hardware.opengl.enable = mkDefault true; + fonts.enableDefaultFonts = mkDefault true; + programs.dconf.enable = mkDefault true; + # To make a Sway session available if a display manager like SDDM is enabled: + services.xserver.displayManager.sessionPackages = [ swayPackage ]; + programs.xwayland.enable = mkDefault true; + # For screen sharing (this option only has an effect with xdg.portal.enable): + xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-wlr ]; + }; + + meta.maintainers = with lib.maintainers; [ primeos colemickens ]; +} diff --git a/nixos/modules/programs/sysdig.nix b/nixos/modules/programs/sysdig.nix new file mode 100644 index 00000000000..fbbf2906556 --- /dev/null +++ b/nixos/modules/programs/sysdig.nix @@ -0,0 +1,14 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.sysdig; +in { + options.programs.sysdig.enable = mkEnableOption "sysdig"; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.sysdig ]; + boot.extraModulePackages = [ config.boot.kernelPackages.sysdig ]; + }; +} diff --git a/nixos/modules/programs/system-config-printer.nix b/nixos/modules/programs/system-config-printer.nix new file mode 100644 index 00000000000..34592dd7064 --- /dev/null +++ b/nixos/modules/programs/system-config-printer.nix @@ -0,0 +1,32 @@ +{ config, pkgs, lib, ... }: + +with lib; + +{ + + ###### interface + + options = { + + programs.system-config-printer = { + + enable = mkEnableOption "system-config-printer, a Graphical user interface for CUPS administration"; + + }; + + }; + + + ###### implementation + + config = mkIf config.programs.system-config-printer.enable { + + environment.systemPackages = [ + pkgs.system-config-printer + ]; + + services.system-config-printer.enable = true; + + }; + +} diff --git a/nixos/modules/programs/systemtap.nix b/nixos/modules/programs/systemtap.nix new file mode 100644 index 00000000000..360e106678e --- /dev/null +++ b/nixos/modules/programs/systemtap.nix @@ -0,0 +1,29 @@ +{ config, lib, ... }: + +with lib; + +let cfg = config.programs.systemtap; +in { + + options = { + programs.systemtap = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Install <command>systemtap</command> along with necessary kernel options. + ''; + }; + }; + }; + config = mkIf cfg.enable { + system.requiredKernelConfig = with config.lib.kernelConfig; [ + (isYes "DEBUG") + ]; + boot.kernel.features.debug = true; + environment.systemPackages = [ + config.boot.kernelPackages.systemtap + ]; + }; + +} diff --git a/nixos/modules/programs/thefuck.nix b/nixos/modules/programs/thefuck.nix new file mode 100644 index 00000000000..b909916158d --- /dev/null +++ b/nixos/modules/programs/thefuck.nix @@ -0,0 +1,39 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + prg = config.programs; + cfg = prg.thefuck; + + initScript = '' + eval $(${pkgs.thefuck}/bin/thefuck --alias ${cfg.alias}) + ''; +in + { + options = { + programs.thefuck = { + enable = mkEnableOption "thefuck"; + + alias = mkOption { + default = "fuck"; + type = types.str; + + description = '' + `thefuck` needs an alias to be configured. + The default value is `fuck`, but you can use anything else as well. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ thefuck ]; + + programs.bash.interactiveShellInit = initScript; + programs.zsh.interactiveShellInit = mkIf prg.zsh.enable initScript; + programs.fish.interactiveShellInit = mkIf prg.fish.enable '' + ${pkgs.thefuck}/bin/thefuck --alias | source + ''; + }; + } diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix new file mode 100644 index 00000000000..74b3fbd9ac0 --- /dev/null +++ b/nixos/modules/programs/tmux.nix @@ -0,0 +1,201 @@ +{ config, pkgs, lib, ... }: + +let + inherit (lib) mkOption mkIf types; + + cfg = config.programs.tmux; + + defaultKeyMode = "emacs"; + defaultResize = 5; + defaultShortcut = "b"; + defaultTerminal = "screen"; + + boolToStr = value: if value then "on" else "off"; + + tmuxConf = '' + set -g default-terminal "${cfg.terminal}" + set -g base-index ${toString cfg.baseIndex} + setw -g pane-base-index ${toString cfg.baseIndex} + + ${if cfg.newSession then "new-session" else ""} + + ${if cfg.reverseSplit then '' + bind v split-window -h + bind s split-window -v + '' else ""} + + set -g status-keys ${cfg.keyMode} + set -g mode-keys ${cfg.keyMode} + + ${if cfg.keyMode == "vi" && cfg.customPaneNavigationAndResize then '' + bind h select-pane -L + bind j select-pane -D + bind k select-pane -U + bind l select-pane -R + + bind -r H resize-pane -L ${toString cfg.resizeAmount} + bind -r J resize-pane -D ${toString cfg.resizeAmount} + bind -r K resize-pane -U ${toString cfg.resizeAmount} + bind -r L resize-pane -R ${toString cfg.resizeAmount} + '' else ""} + + ${if (cfg.shortcut != defaultShortcut) then '' + # rebind main key: C-${cfg.shortcut} + unbind C-${defaultShortcut} + set -g prefix C-${cfg.shortcut} + bind ${cfg.shortcut} send-prefix + bind C-${cfg.shortcut} last-window + '' else ""} + + setw -g aggressive-resize ${boolToStr cfg.aggressiveResize} + setw -g clock-mode-style ${if cfg.clock24 then "24" else "12"} + set -s escape-time ${toString cfg.escapeTime} + set -g history-limit ${toString cfg.historyLimit} + + ${lib.optionalString (cfg.plugins != []) '' + # Run plugins + ${lib.concatMapStringsSep "\n" (x: "run-shell ${x.rtp}") cfg.plugins} + + ''} + + ${cfg.extraConfig} + ''; + +in { + ###### interface + + options = { + programs.tmux = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whenever to configure <command>tmux</command> system-wide."; + relatedPackages = [ "tmux" ]; + }; + + aggressiveResize = mkOption { + default = false; + type = types.bool; + description = '' + Resize the window to the size of the smallest session for which it is the current window. + ''; + }; + + baseIndex = mkOption { + default = 0; + example = 1; + type = types.int; + description = "Base index for windows and panes."; + }; + + clock24 = mkOption { + default = false; + type = types.bool; + description = "Use 24 hour clock."; + }; + + customPaneNavigationAndResize = mkOption { + default = false; + type = types.bool; + description = "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode."; + }; + + escapeTime = mkOption { + default = 500; + example = 0; + type = types.int; + description = "Time in milliseconds for which tmux waits after an escape is input."; + }; + + extraConfig = mkOption { + default = ""; + description = '' + Additional contents of /etc/tmux.conf + ''; + type = types.lines; + }; + + historyLimit = mkOption { + default = 2000; + example = 5000; + type = types.int; + description = "Maximum number of lines held in window history."; + }; + + keyMode = mkOption { + default = defaultKeyMode; + example = "vi"; + type = types.enum [ "emacs" "vi" ]; + description = "VI or Emacs style shortcuts."; + }; + + newSession = mkOption { + default = false; + type = types.bool; + description = "Automatically spawn a session if trying to attach and none are running."; + }; + + reverseSplit = mkOption { + default = false; + type = types.bool; + description = "Reverse the window split shortcuts."; + }; + + resizeAmount = mkOption { + default = defaultResize; + example = 10; + type = types.int; + description = "Number of lines/columns when resizing."; + }; + + shortcut = mkOption { + default = defaultShortcut; + example = "a"; + type = types.str; + description = "Ctrl following by this key is used as the main shortcut."; + }; + + terminal = mkOption { + default = defaultTerminal; + example = "screen-256color"; + type = types.str; + description = "Set the $TERM variable."; + }; + + secureSocket = mkOption { + default = true; + type = types.bool; + description = '' + Store tmux socket under /run, which is more secure than /tmp, but as a + downside it doesn't survive user logout. + ''; + }; + + plugins = mkOption { + default = []; + type = types.listOf types.package; + description = "List of plugins to install."; + example = lib.literalExpression "[ pkgs.tmuxPlugins.nord ]"; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + environment = { + etc."tmux.conf".text = tmuxConf; + + systemPackages = [ pkgs.tmux ] ++ cfg.plugins; + + variables = { + TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/$(id -u)"}''; + }; + }; + }; + + imports = [ + (lib.mkRenamedOptionModule [ "programs" "tmux" "extraTmuxConf" ] [ "programs" "tmux" "extraConfig" ]) + ]; +} diff --git a/nixos/modules/programs/traceroute.nix b/nixos/modules/programs/traceroute.nix new file mode 100644 index 00000000000..6e04057ac50 --- /dev/null +++ b/nixos/modules/programs/traceroute.nix @@ -0,0 +1,28 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.traceroute; +in { + options = { + programs.traceroute = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure a setcap wrapper for traceroute. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + security.wrappers.traceroute = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = "${pkgs.traceroute}/bin/traceroute"; + }; + }; +} diff --git a/nixos/modules/programs/tsm-client.nix b/nixos/modules/programs/tsm-client.nix new file mode 100644 index 00000000000..28db9625387 --- /dev/null +++ b/nixos/modules/programs/tsm-client.nix @@ -0,0 +1,287 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (builtins) length map; + inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs mapAttrsToList optionalAttrs; + inherit (lib.modules) mkDefault mkIf; + inherit (lib.options) literalExpression mkEnableOption mkOption; + inherit (lib.strings) concatStringsSep optionalString toLower; + inherit (lib.types) addCheck attrsOf lines nonEmptyStr nullOr package path port str strMatching submodule; + + # Checks if given list of strings contains unique + # elements when compared without considering case. + # Type: checkIUnique :: [string] -> bool + # Example: checkIUnique ["foo" "Foo"] => false + checkIUnique = lst: + let + lenUniq = l: length (lib.lists.unique l); + in + lenUniq lst == lenUniq (map toLower lst); + + # TSM rejects servername strings longer than 64 chars. + servernameType = strMatching ".{1,64}"; + + serverOptions = { name, config, ... }: { + options.name = mkOption { + type = servernameType; + example = "mainTsmServer"; + description = '' + Local name of the IBM TSM server, + must be uncapitalized and no longer than 64 chars. + The value will be used for the + <literal>server</literal> + directive in <filename>dsm.sys</filename>. + ''; + }; + options.server = mkOption { + type = nonEmptyStr; + example = "tsmserver.company.com"; + description = '' + Host/domain name or IP address of the IBM TSM server. + The value will be used for the + <literal>tcpserveraddress</literal> + directive in <filename>dsm.sys</filename>. + ''; + }; + options.port = mkOption { + type = addCheck port (p: p<=32767); + default = 1500; # official default + description = '' + TCP port of the IBM TSM server. + The value will be used for the + <literal>tcpport</literal> + directive in <filename>dsm.sys</filename>. + TSM does not support ports above 32767. + ''; + }; + options.node = mkOption { + type = nonEmptyStr; + example = "MY-TSM-NODE"; + description = '' + Target node name on the IBM TSM server. + The value will be used for the + <literal>nodename</literal> + directive in <filename>dsm.sys</filename>. + ''; + }; + options.genPasswd = mkEnableOption '' + automatic client password generation. + This option influences the + <literal>passwordaccess</literal> + directive in <filename>dsm.sys</filename>. + The password will be stored in the directory + given by the option <option>passwdDir</option>. + <emphasis>Caution</emphasis>: + If this option is enabled and the server forces + to renew the password (e.g. on first connection), + a random password will be generated and stored + ''; + options.passwdDir = mkOption { + type = path; + example = "/home/alice/tsm-password"; + description = '' + Directory that holds the TSM + node's password information. + The value will be used for the + <literal>passworddir</literal> + directive in <filename>dsm.sys</filename>. + ''; + }; + options.includeExclude = mkOption { + type = lines; + default = ""; + example = '' + exclude.dir /nix/store + include.encrypt /home/.../* + ''; + description = '' + <literal>include.*</literal> and + <literal>exclude.*</literal> directives to be + used when sending files to the IBM TSM server. + The lines will be written into a file that the + <literal>inclexcl</literal> + directive in <filename>dsm.sys</filename> points to. + ''; + }; + options.extraConfig = mkOption { + # TSM option keys are case insensitive; + # we have to ensure there are no keys that + # differ only by upper and lower case. + type = addCheck + (attrsOf (nullOr str)) + (attrs: checkIUnique (attrNames attrs)); + default = {}; + example.compression = "yes"; + example.passwordaccess = null; + description = '' + Additional key-value pairs for the server stanza. + Values must be strings, or <literal>null</literal> + for the key not to be used in the stanza + (e.g. to overrule values generated by other options). + ''; + }; + options.text = mkOption { + type = lines; + example = literalExpression + ''lib.modules.mkAfter "compression no"''; + description = '' + Additional text lines for the server stanza. + This option can be used if certion configuration keys + must be used multiple times or ordered in a certain way + as the <option>extraConfig</option> option can't + control the order of lines in the resulting stanza. + Note that the <literal>server</literal> + line at the beginning of the stanza is + not part of this option's value. + ''; + }; + options.stanza = mkOption { + type = str; + internal = true; + visible = false; + description = "Server stanza text generated from the options."; + }; + config.name = mkDefault name; + # Client system-options file directives are explained here: + # https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=commands-processing-options + config.extraConfig = + mapAttrs (lib.trivial.const mkDefault) ( + { + commmethod = "v6tcpip"; # uses v4 or v6, based on dns lookup result + tcpserveraddress = config.server; + tcpport = builtins.toString config.port; + nodename = config.node; + passwordaccess = if config.genPasswd then "generate" else "prompt"; + passworddir = ''"${config.passwdDir}"''; + } // optionalAttrs (config.includeExclude!="") { + inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"''; + } + ); + config.text = + let + attrset = filterAttrs (k: v: v!=null) config.extraConfig; + mkLine = k: v: k + optionalString (v!="") " ${v}"; + lines = mapAttrsToList mkLine attrset; + in + concatStringsSep "\n" lines; + config.stanza = '' + server ${config.name} + ${config.text} + ''; + }; + + options.programs.tsmClient = { + enable = mkEnableOption '' + IBM Spectrum Protect (Tivoli Storage Manager, TSM) + client command line applications with a + client system-options file "dsm.sys" + ''; + servers = mkOption { + type = attrsOf (submodule [ serverOptions ]); + default = {}; + example.mainTsmServer = { + server = "tsmserver.company.com"; + node = "MY-TSM-NODE"; + extraConfig.compression = "yes"; + }; + description = '' + Server definitions ("stanzas") + for the client system-options file. + ''; + }; + defaultServername = mkOption { + type = nullOr servernameType; + default = null; + example = "mainTsmServer"; + description = '' + If multiple server stanzas are declared with + <option>programs.tsmClient.servers</option>, + this option may be used to name a default + server stanza that IBM TSM uses in the absence of + a user-defined <filename>dsm.opt</filename> file. + This option translates to a + <literal>defaultserver</literal> configuration line. + ''; + }; + dsmSysText = mkOption { + type = lines; + readOnly = true; + description = '' + This configuration key contains the effective text + of the client system-options file "dsm.sys". + It should not be changed, but may be + used to feed the configuration into other + TSM-depending packages used on the system. + ''; + }; + package = mkOption { + type = package; + default = pkgs.tsm-client; + defaultText = literalExpression "pkgs.tsm-client"; + example = literalExpression "pkgs.tsm-client-withGui"; + description = '' + The TSM client derivation to be + added to the system environment. + It will called with <literal>.override</literal> + to add paths to the client system-options file. + ''; + }; + wrappedPackage = mkOption { + type = package; + readOnly = true; + description = '' + The TSM client derivation, wrapped with the path + to the client system-options file "dsm.sys". + This option is to provide the effective derivation + for other modules that want to call TSM executables. + ''; + }; + }; + + cfg = config.programs.tsmClient; + + assertions = [ + { + assertion = checkIUnique (mapAttrsToList (k: v: v.name) cfg.servers); + message = '' + TSM servernames contain duplicate name + (note that case doesn't matter!) + ''; + } + { + assertion = (cfg.defaultServername!=null)->(hasAttr cfg.defaultServername cfg.servers); + message = "TSM defaultServername not found in list of servers"; + } + ]; + + dsmSysText = '' + **** IBM Spectrum Protect (Tivoli Storage Manager) + **** client system-options file "dsm.sys". + **** Do not edit! + **** This file is generated by NixOS configuration. + + ${optionalString (cfg.defaultServername!=null) "defaultserver ${cfg.defaultServername}"} + + ${concatStringsSep "\n" (mapAttrsToList (k: v: v.stanza) cfg.servers)} + ''; + +in + +{ + + inherit options; + + config = mkIf cfg.enable { + inherit assertions; + programs.tsmClient.dsmSysText = dsmSysText; + programs.tsmClient.wrappedPackage = cfg.package.override rec { + dsmSysCli = pkgs.writeText "dsm.sys" cfg.dsmSysText; + dsmSysApi = dsmSysCli; + }; + environment.systemPackages = [ cfg.wrappedPackage ]; + }; + + meta.maintainers = [ lib.maintainers.yarny ]; + +} diff --git a/nixos/modules/programs/turbovnc.nix b/nixos/modules/programs/turbovnc.nix new file mode 100644 index 00000000000..e6f8836aa36 --- /dev/null +++ b/nixos/modules/programs/turbovnc.nix @@ -0,0 +1,54 @@ +# Global configuration for the SSH client. + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.turbovnc; +in +{ + options = { + + programs.turbovnc = { + + ensureHeadlessSoftwareOpenGL = mkOption { + type = types.bool; + default = false; + description = '' + Whether to set up NixOS such that TurboVNC's built-in software OpenGL + implementation works. + + This will enable <option>hardware.opengl.enable</option> so that OpenGL + programs can find Mesa's llvmpipe drivers. + + Setting this option to <code>false</code> does not mean that software + OpenGL won't work; it may still work depending on your system + configuration. + + This option is also intended to generate warnings if you are using some + configuration that's incompatible with using headless software OpenGL + in TurboVNC. + ''; + }; + + }; + + }; + + config = mkIf cfg.ensureHeadlessSoftwareOpenGL { + + # TurboVNC has builtin support for Mesa llvmpipe's `swrast` + # software rendering to implemnt GLX (OpenGL on Xorg). + # However, just building TurboVNC with support for that is not enough + # (it only takes care of the X server side part of OpenGL); + # the indiviudual applications (e.g. `glxgears`) also need to directly load + # the OpenGL libs. + # Thus, this creates `/run/opengl-driver` populated by Mesa so that the applications + # can find the llvmpipe `swrast.so` software rendering DRI lib via `libglvnd`. + # This comment exists to explain why `hardware.` is involved, + # even though 100% software rendering is used. + hardware.opengl.enable = true; + + }; +} diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix new file mode 100644 index 00000000000..0dc08c435df --- /dev/null +++ b/nixos/modules/programs/udevil.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.udevil; + +in { + options.programs.udevil.enable = mkEnableOption "udevil"; + + config = mkIf cfg.enable { + security.wrappers.udevil = + { setuid = true; + owner = "root"; + group = "root"; + source = "${lib.getBin pkgs.udevil}/bin/udevil"; + }; + }; +} diff --git a/nixos/modules/programs/usbtop.nix b/nixos/modules/programs/usbtop.nix new file mode 100644 index 00000000000..c1b6ee38caa --- /dev/null +++ b/nixos/modules/programs/usbtop.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.usbtop; +in { + options = { + programs.usbtop.enable = mkEnableOption "usbtop and required kernel module"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + usbtop + ]; + + boot.kernelModules = [ + "usbmon" + ]; + }; +} diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix new file mode 100644 index 00000000000..1695bc99473 --- /dev/null +++ b/nixos/modules/programs/vim.nix @@ -0,0 +1,33 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.vim; +in { + options.programs.vim = { + defaultEditor = mkOption { + type = types.bool; + default = false; + description = '' + When enabled, installs vim and configures vim to be the default editor + using the EDITOR environment variable. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.vim; + defaultText = literalExpression "pkgs.vim"; + example = literalExpression "pkgs.vimHugeX"; + description = '' + vim package to use. + ''; + }; + }; + + config = mkIf cfg.defaultEditor { + environment.systemPackages = [ cfg.package ]; + environment.variables = { EDITOR = mkOverride 900 "vim"; }; + }; +} diff --git a/nixos/modules/programs/virtualbox.nix b/nixos/modules/programs/virtualbox.nix new file mode 100644 index 00000000000..be96cf23b39 --- /dev/null +++ b/nixos/modules/programs/virtualbox.nix @@ -0,0 +1,8 @@ +let + msg = "Importing <nixpkgs/nixos/modules/programs/virtualbox.nix> is " + + "deprecated, please use `virtualisation.virtualbox.host.enable = true' " + + "instead."; +in { + config.warnings = [ msg ]; + config.virtualisation.virtualbox.host.enable = true; +} diff --git a/nixos/modules/programs/wavemon.nix b/nixos/modules/programs/wavemon.nix new file mode 100644 index 00000000000..e5ccacba75d --- /dev/null +++ b/nixos/modules/programs/wavemon.nix @@ -0,0 +1,30 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.wavemon; +in { + options = { + programs.wavemon = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add wavemon to the global environment and configure a + setcap wrapper for it. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ wavemon ]; + security.wrappers.wavemon = { + owner = "root"; + group = "root"; + capabilities = "cap_net_admin+ep"; + source = "${pkgs.wavemon}/bin/wavemon"; + }; + }; +} diff --git a/nixos/modules/programs/waybar.nix b/nixos/modules/programs/waybar.nix new file mode 100644 index 00000000000..22530e6c7d4 --- /dev/null +++ b/nixos/modules/programs/waybar.nix @@ -0,0 +1,20 @@ +{ lib, pkgs, config, ... }: + +with lib; + +{ + options.programs.waybar = { + enable = mkEnableOption "waybar"; + }; + + config = mkIf config.programs.waybar.enable { + systemd.user.services.waybar = { + description = "Waybar as systemd service"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + script = "${pkgs.waybar}/bin/waybar"; + }; + }; + + meta.maintainers = [ maintainers.FlorianFranzen ]; +} diff --git a/nixos/modules/programs/weylus.nix b/nixos/modules/programs/weylus.nix new file mode 100644 index 00000000000..ea92c77e7c3 --- /dev/null +++ b/nixos/modules/programs/weylus.nix @@ -0,0 +1,47 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.weylus; +in +{ + options.programs.weylus = with types; { + enable = mkEnableOption "weylus"; + + openFirewall = mkOption { + type = bool; + default = false; + description = '' + Open ports needed for the functionality of the program. + ''; + }; + + users = mkOption { + type = listOf str; + default = [ ]; + description = '' + To enable stylus and multi-touch support, the user you're going to use must be added to this list. + These users can synthesize input events system-wide, even when another user is logged in - untrusted users should not be added. + ''; + }; + + package = mkOption { + type = package; + default = pkgs.weylus; + defaultText = "pkgs.weylus"; + description = "Weylus package to install."; + }; + }; + config = mkIf cfg.enable { + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ 1701 9001 ]; + }; + + hardware.uinput.enable = true; + + users.groups.uinput.members = cfg.users; + + environment.systemPackages = [ cfg.package ]; + }; +} diff --git a/nixos/modules/programs/wireshark.nix b/nixos/modules/programs/wireshark.nix new file mode 100644 index 00000000000..f7b0727cb2b --- /dev/null +++ b/nixos/modules/programs/wireshark.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.wireshark; + wireshark = cfg.package; +in { + options = { + programs.wireshark = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add Wireshark to the global environment and configure a + setcap wrapper for 'dumpcap' for users in the 'wireshark' group. + ''; + }; + package = mkOption { + type = types.package; + default = pkgs.wireshark-cli; + defaultText = literalExpression "pkgs.wireshark-cli"; + description = '' + Which Wireshark package to install in the global environment. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ wireshark ]; + users.groups.wireshark = {}; + + security.wrappers.dumpcap = { + source = "${wireshark}/bin/dumpcap"; + capabilities = "cap_net_raw+p"; + owner = "root"; + group = "wireshark"; + permissions = "u+rx,g+x"; + }; + }; +} diff --git a/nixos/modules/programs/wshowkeys.nix b/nixos/modules/programs/wshowkeys.nix new file mode 100644 index 00000000000..f7b71d2bb0c --- /dev/null +++ b/nixos/modules/programs/wshowkeys.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.wshowkeys; +in { + meta.maintainers = with maintainers; [ primeos ]; + + options = { + programs.wshowkeys = { + enable = mkEnableOption '' + wshowkeys (displays keypresses on screen on supported Wayland + compositors). It requires root permissions to read input events, but + these permissions are dropped after startup''; + }; + }; + + config = mkIf cfg.enable { + security.wrappers.wshowkeys = + { setuid = true; + owner = "root"; + group = "root"; + source = "${pkgs.wshowkeys}/bin/wshowkeys"; + }; + }; +} diff --git a/nixos/modules/programs/xfs_quota.nix b/nixos/modules/programs/xfs_quota.nix new file mode 100644 index 00000000000..c03e59a5b4a --- /dev/null +++ b/nixos/modules/programs/xfs_quota.nix @@ -0,0 +1,110 @@ +# Configuration for the xfs_quota command + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.xfs_quota; + + limitOptions = opts: concatStringsSep " " [ + (optionalString (opts.sizeSoftLimit != null) "bsoft=${opts.sizeSoftLimit}") + (optionalString (opts.sizeHardLimit != null) "bhard=${opts.sizeHardLimit}") + ]; + +in + +{ + + ###### interface + + options = { + + programs.xfs_quota = { + projects = mkOption { + default = {}; + type = types.attrsOf (types.submodule { + options = { + id = mkOption { + type = types.int; + description = "Project ID."; + }; + + fileSystem = mkOption { + type = types.str; + description = "XFS filesystem hosting the xfs_quota project."; + default = "/"; + }; + + path = mkOption { + type = types.str; + description = "Project directory."; + }; + + sizeSoftLimit = mkOption { + type = types.nullOr types.str; + default = null; + example = "30g"; + description = "Soft limit of the project size"; + }; + + sizeHardLimit = mkOption { + type = types.nullOr types.str; + default = null; + example = "50g"; + description = "Hard limit of the project size."; + }; + }; + }); + + description = "Setup of xfs_quota projects. Make sure the filesystem is mounted with the pquota option."; + + example = { + projname = { + id = 50; + path = "/xfsprojects/projname"; + sizeHardLimit = "50g"; + }; + }; + }; + }; + + }; + + + ###### implementation + + config = mkIf (cfg.projects != {}) { + + environment.etc.projects.source = pkgs.writeText "etc-project" + (concatStringsSep "\n" (mapAttrsToList + (name: opts: "${toString opts.id}:${opts.path}") cfg.projects)); + + environment.etc.projid.source = pkgs.writeText "etc-projid" + (concatStringsSep "\n" (mapAttrsToList + (name: opts: "${name}:${toString opts.id}") cfg.projects)); + + systemd.services = mapAttrs' (name: opts: + nameValuePair "xfs_quota-${name}" { + description = "Setup xfs_quota for project ${name}"; + script = '' + ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'project -s ${name}' ${opts.fileSystem} + ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'limit -p ${limitOptions opts} ${name}' ${opts.fileSystem} + ''; + + wantedBy = [ "multi-user.target" ]; + after = [ ((replaceChars [ "/" ] [ "-" ] opts.fileSystem) + ".mount") ]; + + restartTriggers = [ config.environment.etc.projects.source ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + } + ) cfg.projects; + + }; + +} diff --git a/nixos/modules/programs/xonsh.nix b/nixos/modules/programs/xonsh.nix new file mode 100644 index 00000000000..6e40db51cdb --- /dev/null +++ b/nixos/modules/programs/xonsh.nix @@ -0,0 +1,86 @@ +# This module defines global configuration for the xonsh. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.xonsh; + +in + +{ + + options = { + + programs.xonsh = { + + enable = mkOption { + default = false; + description = '' + Whether to configure xonsh as an interactive shell. + ''; + type = types.bool; + }; + + package = mkOption { + type = types.package; + default = pkgs.xonsh; + defaultText = literalExpression "pkgs.xonsh"; + example = literalExpression "pkgs.xonsh.override { configFile = \"/path/to/xonshrc\"; }"; + description = '' + xonsh package to use. + ''; + }; + + config = mkOption { + default = ""; + description = "Control file to customize your shell behavior."; + type = types.lines; + }; + + }; + + }; + + config = mkIf cfg.enable { + + environment.etc.xonshrc.text = '' + # /etc/xonshrc: DO NOT EDIT -- this file has been generated automatically. + + + if not ''${...}.get('__NIXOS_SET_ENVIRONMENT_DONE'): + # The NixOS environment and thereby also $PATH + # haven't been fully set up at this point. But + # `source-bash` below requires `bash` to be on $PATH, + # so add an entry with bash's location: + $PATH.add('${pkgs.bash}/bin') + + # Stash xonsh's ls alias, so that we don't get a collision + # with Bash's ls alias from environment.shellAliases: + _ls_alias = aliases.pop('ls', None) + + # Source the NixOS environment config. + source-bash "${config.system.build.setEnvironment}" + + # Restore xonsh's ls alias, overriding that from Bash (if any). + if _ls_alias is not None: + aliases['ls'] = _ls_alias + del _ls_alias + + + ${cfg.config} + ''; + + environment.systemPackages = [ cfg.package ]; + + environment.shells = + [ "/run/current-system/sw/bin/xonsh" + "${cfg.package}/bin/xonsh" + ]; + + }; + +} + diff --git a/nixos/modules/programs/xss-lock.nix b/nixos/modules/programs/xss-lock.nix new file mode 100644 index 00000000000..aba76133e5e --- /dev/null +++ b/nixos/modules/programs/xss-lock.nix @@ -0,0 +1,45 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.xss-lock; +in +{ + options.programs.xss-lock = { + enable = mkEnableOption "xss-lock"; + + lockerCommand = mkOption { + default = "${pkgs.i3lock}/bin/i3lock"; + defaultText = literalExpression ''"''${pkgs.i3lock}/bin/i3lock"''; + example = literalExpression ''"''${pkgs.i3lock-fancy}/bin/i3lock-fancy"''; + type = types.separatedString " "; + description = "Locker to be used with xsslock"; + }; + + extraOptions = mkOption { + default = [ ]; + example = [ "--ignore-sleep" ]; + type = types.listOf types.str; + description = '' + Additional command-line arguments to pass to + <command>xss-lock</command>. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.user.services.xss-lock = { + description = "XSS Lock Daemon"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + serviceConfig.ExecStart = with lib; + strings.concatStringsSep " " ([ + "${pkgs.xss-lock}/bin/xss-lock" "--session \${XDG_SESSION_ID}" + ] ++ (map escapeShellArg cfg.extraOptions) ++ [ + "--" + cfg.lockerCommand + ]); + }; + }; +} diff --git a/nixos/modules/programs/xwayland.nix b/nixos/modules/programs/xwayland.nix new file mode 100644 index 00000000000..3a8080fa4c4 --- /dev/null +++ b/nixos/modules/programs/xwayland.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.xwayland; + +in + +{ + options.programs.xwayland = { + + enable = mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)"; + + defaultFontPath = mkOption { + type = types.str; + default = optionalString config.fonts.fontDir.enable + "/run/current-system/sw/share/X11/fonts"; + defaultText = literalExpression '' + optionalString config.fonts.fontDir.enable "/run/current-system/sw/share/X11/fonts" + ''; + description = '' + Default font path. Setting this option causes Xwayland to be rebuilt. + ''; + }; + + package = mkOption { + type = types.path; + default = pkgs.xwayland.override (oldArgs: { + inherit (cfg) defaultFontPath; + }); + defaultText = literalExpression '' + pkgs.xwayland.override (oldArgs: { + inherit (config.programs.xwayland) defaultFontPath; + }) + ''; + description = "The Xwayland package to use."; + }; + + }; + + config = mkIf cfg.enable { + + # Needed by some applications for fonts and default settings + environment.pathsToLink = [ "/share/X11" ]; + + environment.systemPackages = [ cfg.package ]; + + }; +} diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix new file mode 100644 index 00000000000..a8fac41e899 --- /dev/null +++ b/nixos/modules/programs/yabar.nix @@ -0,0 +1,163 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + cfg = config.programs.yabar; + + mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList ( + key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};" + ) v); + + listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r)); + + configFile = let + bars = mapAttrsToList ( + name: cfg: '' + ${name}: { + font: "${cfg.font}"; + position: "${cfg.position}"; + + ${mapExtra cfg.extra} + + block-list: [${listKeys cfg.indicators}] + + ${concatStringsSep "\n" (mapAttrsToList ( + name: cfg: '' + ${name}: { + exec: "${cfg.exec}"; + align: "${cfg.align}"; + ${mapExtra cfg.extra} + }; + '' + ) cfg.indicators)} + }; + '' + ) cfg.bars; + in pkgs.writeText "yabar.conf" '' + bar-list = [${listKeys cfg.bars}]; + ${concatStringsSep "\n" bars} + ''; +in + { + options.programs.yabar = { + enable = mkEnableOption "yabar"; + + package = mkOption { + default = pkgs.yabar-unstable; + defaultText = literalExpression "pkgs.yabar-unstable"; + example = literalExpression "pkgs.yabar"; + type = types.package; + + # `yabar-stable` segfaults under certain conditions. + apply = x: if x == pkgs.yabar-unstable then x else flip warn x '' + It's not recommended to use `yabar' with `programs.yabar', the (old) stable release + tends to segfault under certain circumstances: + + * https://github.com/geommer/yabar/issues/86 + * https://github.com/geommer/yabar/issues/68 + * https://github.com/geommer/yabar/issues/143 + + Most of them don't occur on master anymore, until a new release is published, it's recommended + to use `yabar-unstable'. + ''; + + description = '' + The package which contains the `yabar` binary. + + Nixpkgs provides the `yabar` and `yabar-unstable` + derivations since 18.03, so it's possible to choose. + ''; + }; + + bars = mkOption { + default = {}; + type = types.attrsOf(types.submodule { + options = { + font = mkOption { + default = "sans bold 9"; + example = "Droid Sans, FontAwesome Bold 9"; + type = types.str; + + description = '' + The font that will be used to draw the status bar. + ''; + }; + + position = mkOption { + default = "top"; + example = "bottom"; + type = types.enum [ "top" "bottom" ]; + + description = '' + The position where the bar will be rendered. + ''; + }; + + extra = mkOption { + default = {}; + type = types.attrsOf types.str; + + description = '' + An attribute set which contains further attributes of a bar. + ''; + }; + + indicators = mkOption { + default = {}; + type = types.attrsOf(types.submodule { + options.exec = mkOption { + example = "YABAR_DATE"; + type = types.str; + description = '' + The type of the indicator to be executed. + ''; + }; + + options.align = mkOption { + default = "left"; + example = "right"; + type = types.enum [ "left" "center" "right" ]; + + description = '' + Whether to align the indicator at the left or right of the bar. + ''; + }; + + options.extra = mkOption { + default = {}; + type = types.attrsOf (types.either types.str types.int); + + description = '' + An attribute set which contains further attributes of a indicator. + ''; + }; + }); + + description = '' + Indicators that should be rendered by yabar. + ''; + }; + }; + }); + + description = '' + List of bars that should be rendered by yabar. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.user.services.yabar = { + description = "yabar service"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + + script = '' + ${cfg.package}/bin/yabar -c ${configFile} + ''; + + serviceConfig.Restart = "always"; + }; + }; + } diff --git a/nixos/modules/programs/zmap.nix b/nixos/modules/programs/zmap.nix new file mode 100644 index 00000000000..2e27fce4d7c --- /dev/null +++ b/nixos/modules/programs/zmap.nix @@ -0,0 +1,18 @@ +{ pkgs, config, lib, ... }: + +with lib; + +let + cfg = config.programs.zmap; +in { + options.programs.zmap = { + enable = mkEnableOption "ZMap"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.zmap ]; + + environment.etc."zmap/blacklist.conf".source = "${pkgs.zmap}/etc/zmap/blacklist.conf"; + environment.etc."zmap/zmap.conf".source = "${pkgs.zmap}/etc/zmap.conf"; + }; +} diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix new file mode 100644 index 00000000000..9d7622bd328 --- /dev/null +++ b/nixos/modules/programs/zsh/oh-my-zsh.nix @@ -0,0 +1,146 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.zsh.ohMyZsh; + + mkLinkFarmEntry = name: dir: + let + env = pkgs.buildEnv { + name = "zsh-${name}-env"; + paths = cfg.customPkgs; + pathsToLink = "/share/zsh/${dir}"; + }; + in + { inherit name; path = "${env}/share/zsh/${dir}"; }; + + mkLinkFarmEntry' = name: mkLinkFarmEntry name name; + + custom = + if cfg.custom != null then cfg.custom + else if length cfg.customPkgs == 0 then null + else pkgs.linkFarm "oh-my-zsh-custom" [ + (mkLinkFarmEntry' "themes") + (mkLinkFarmEntry "completions" "site-functions") + (mkLinkFarmEntry' "plugins") + ]; + +in + { + imports = [ + (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "enable" ] [ "programs" "zsh" "ohMyZsh" "enable" ]) + (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ]) + (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ]) + (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ]) + ]; + + options = { + programs.zsh.ohMyZsh = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable oh-my-zsh. + ''; + }; + + package = mkOption { + default = pkgs.oh-my-zsh; + defaultText = literalExpression "pkgs.oh-my-zsh"; + description = '' + Package to install for `oh-my-zsh` usage. + ''; + + type = types.package; + }; + + plugins = mkOption { + default = []; + type = types.listOf(types.str); + description = '' + List of oh-my-zsh plugins + ''; + }; + + custom = mkOption { + default = null; + type = with types; nullOr str; + description = '' + Path to a custom oh-my-zsh package to override config of oh-my-zsh. + (Can't be used along with `customPkgs`). + ''; + }; + + customPkgs = mkOption { + default = []; + type = types.listOf types.package; + description = '' + List of custom packages that should be loaded into `oh-my-zsh`. + ''; + }; + + theme = mkOption { + default = ""; + type = types.str; + description = '' + Name of the theme to be used by oh-my-zsh. + ''; + }; + + cacheDir = mkOption { + default = "$HOME/.cache/oh-my-zsh"; + type = types.str; + description = '' + Cache directory to be used by `oh-my-zsh`. + Without this option it would default to the read-only nix store. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + # Prevent zsh from overwriting oh-my-zsh's prompt + programs.zsh.promptInit = mkDefault ""; + + environment.systemPackages = [ cfg.package ]; + + programs.zsh.interactiveShellInit = '' + # oh-my-zsh configuration generated by NixOS + export ZSH=${cfg.package}/share/oh-my-zsh + + ${optionalString (length(cfg.plugins) > 0) + "plugins=(${concatStringsSep " " cfg.plugins})" + } + + ${optionalString (custom != null) + "ZSH_CUSTOM=\"${custom}\"" + } + + ${optionalString (stringLength(cfg.theme) > 0) + "ZSH_THEME=\"${cfg.theme}\"" + } + + ${optionalString (cfg.cacheDir != null) '' + if [[ ! -d "${cfg.cacheDir}" ]]; then + mkdir -p "${cfg.cacheDir}" + fi + ZSH_CACHE_DIR=${cfg.cacheDir} + ''} + + source $ZSH/oh-my-zsh.sh + ''; + + assertions = [ + { + assertion = cfg.custom != null -> cfg.customPkgs == []; + message = "If `cfg.custom` is set for `ZSH_CUSTOM`, `customPkgs` can't be used!"; + } + ]; + + }; + + meta.doc = ./oh-my-zsh.xml; + } diff --git a/nixos/modules/programs/zsh/oh-my-zsh.xml b/nixos/modules/programs/zsh/oh-my-zsh.xml new file mode 100644 index 00000000000..14a7228ad9b --- /dev/null +++ b/nixos/modules/programs/zsh/oh-my-zsh.xml @@ -0,0 +1,155 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-programs-zsh-ohmyzsh"> + <title>Oh my ZSH</title> + <para> + <literal><link xlink:href="https://ohmyz.sh/">oh-my-zsh</link></literal> is a + framework to manage your <link xlink:href="https://www.zsh.org/">ZSH</link> + configuration including completion scripts for several CLI tools or custom + prompt themes. + </para> + <section xml:id="module-programs-oh-my-zsh-usage"> + <title>Basic usage</title> + + <para> + The module uses the <literal>oh-my-zsh</literal> package with all available + features. The initial setup using Nix expressions is fairly similar to the + configuration format of <literal>oh-my-zsh</literal>. +<programlisting> +{ + programs.zsh.ohMyZsh = { + enable = true; + plugins = [ "git" "python" "man" ]; + theme = "agnoster"; + }; +} +</programlisting> + For a detailed explanation of these arguments please refer to the + <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal> + docs</link>. + </para> + + <para> + The expression generates the needed configuration and writes it into your + <literal>/etc/zshrc</literal>. + </para> + </section> + <section xml:id="module-programs-oh-my-zsh-additions"> + <title>Custom additions</title> + + <para> + Sometimes third-party or custom scripts such as a modified theme may be + needed. <literal>oh-my-zsh</literal> provides the + <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link> + environment variable for this which points to a directory with additional + scripts. + </para> + + <para> + The module can do this as well: +<programlisting> +{ + programs.zsh.ohMyZsh.custom = "~/path/to/custom/scripts"; +} +</programlisting> + </para> + </section> + <section xml:id="module-programs-oh-my-zsh-environments"> + <title>Custom environments</title> + + <para> + There are several extensions for <literal>oh-my-zsh</literal> packaged in + <literal>nixpkgs</literal>. One of them is + <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link> + which bundles completion scripts and a plugin for + <literal>oh-my-zsh</literal>. + </para> + + <para> + Rather than using a single mutable path for <literal>ZSH_CUSTOM</literal>, + it's also possible to generate this path from a list of Nix packages: +<programlisting> +{ pkgs, ... }: +{ + programs.zsh.ohMyZsh.customPkgs = [ + pkgs.nix-zsh-completions + # and even more... + ]; +} +</programlisting> + Internally a single store path will be created using + <literal>buildEnv</literal>. Please refer to the docs of + <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link> + for further reference. + </para> + + <para> + <emphasis>Please keep in mind that this is not compatible with + <literal>programs.zsh.ohMyZsh.custom</literal> as it requires an immutable + store path while <literal>custom</literal> shall remain mutable! An + evaluation failure will be thrown if both <literal>custom</literal> and + <literal>customPkgs</literal> are set.</emphasis> + </para> + </section> + <section xml:id="module-programs-oh-my-zsh-packaging-customizations"> + <title>Package your own customizations</title> + + <para> + If third-party customizations (e.g. new themes) are supposed to be added to + <literal>oh-my-zsh</literal> there are several pitfalls to keep in mind: + </para> + + <itemizedlist> + <listitem> + <para> + To comply with the default structure of <literal>ZSH</literal> the entire + output needs to be written to <literal>$out/share/zsh.</literal> + </para> + </listitem> + <listitem> + <para> + Completion scripts are supposed to be stored at + <literal>$out/share/zsh/site-functions</literal>. This directory is part + of the + <literal><link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html">fpath</link></literal> + and the package should be compatible with pure <literal>ZSH</literal> + setups. The module will automatically link the contents of + <literal>site-functions</literal> to completions directory in the proper + store path. + </para> + </listitem> + <listitem> + <para> + The <literal>plugins</literal> directory needs the structure + <literal>pluginname/pluginname.plugin.zsh</literal> as structured in the + <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream + repo.</link> + </para> + </listitem> + </itemizedlist> + + <para> + A derivation for <literal>oh-my-zsh</literal> may look like this: +<programlisting> +{ stdenv, fetchFromGitHub }: + +stdenv.mkDerivation rec { + name = "exemplary-zsh-customization-${version}"; + version = "1.0.0"; + src = fetchFromGitHub { + # path to the upstream repository + }; + + dontBuild = true; + installPhase = '' + mkdir -p $out/share/zsh/site-functions + cp {themes,plugins} $out/share/zsh + cp completions $out/share/zsh/site-functions + ''; +} +</programlisting> + </para> + </section> +</chapter> diff --git a/nixos/modules/programs/zsh/zinputrc b/nixos/modules/programs/zsh/zinputrc new file mode 100644 index 00000000000..6121f3e21f1 --- /dev/null +++ b/nixos/modules/programs/zsh/zinputrc @@ -0,0 +1,42 @@ +# Stolen from ArchWiki + +# create a zkbd compatible hash; +# to add other keys to this hash, see: man 5 terminfo +typeset -A key + +key[Home]=${terminfo[khome]} + +key[End]=${terminfo[kend]} +key[Insert]=${terminfo[kich1]} +key[Delete]=${terminfo[kdch1]} +key[Up]=${terminfo[kcuu1]} +key[Down]=${terminfo[kcud1]} +key[Left]=${terminfo[kcub1]} +key[Right]=${terminfo[kcuf1]} +key[PageUp]=${terminfo[kpp]} +key[PageDown]=${terminfo[knp]} + +# setup key accordingly +[[ -n "${key[Home]}" ]] && bindkey "${key[Home]}" beginning-of-line +[[ -n "${key[End]}" ]] && bindkey "${key[End]}" end-of-line +[[ -n "${key[Insert]}" ]] && bindkey "${key[Insert]}" overwrite-mode +[[ -n "${key[Delete]}" ]] && bindkey "${key[Delete]}" delete-char +[[ -n "${key[Up]}" ]] && bindkey "${key[Up]}" up-line-or-history +[[ -n "${key[Down]}" ]] && bindkey "${key[Down]}" down-line-or-history +[[ -n "${key[Left]}" ]] && bindkey "${key[Left]}" backward-char +[[ -n "${key[Right]}" ]] && bindkey "${key[Right]}" forward-char +[[ -n "${key[PageUp]}" ]] && bindkey "${key[PageUp]}" beginning-of-buffer-or-history +[[ -n "${key[PageDown]}" ]] && bindkey "${key[PageDown]}" end-of-buffer-or-history + +# Finally, make sure the terminal is in application mode, when zle is +# active. Only then are the values from $terminfo valid. +if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then + function zle-line-init () { + printf '%s' "${terminfo[smkx]}" + } + function zle-line-finish () { + printf '%s' "${terminfo[rmkx]}" + } + zle -N zle-line-init + zle -N zle-line-finish +fi diff --git a/nixos/modules/programs/zsh/zsh-autoenv.nix b/nixos/modules/programs/zsh/zsh-autoenv.nix new file mode 100644 index 00000000000..62f497a45dd --- /dev/null +++ b/nixos/modules/programs/zsh/zsh-autoenv.nix @@ -0,0 +1,28 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.zsh.zsh-autoenv; +in { + options = { + programs.zsh.zsh-autoenv = { + enable = mkEnableOption "zsh-autoenv"; + package = mkOption { + default = pkgs.zsh-autoenv; + defaultText = literalExpression "pkgs.zsh-autoenv"; + description = '' + Package to install for `zsh-autoenv` usage. + ''; + + type = types.package; + }; + }; + }; + + config = mkIf cfg.enable { + programs.zsh.interactiveShellInit = '' + source ${cfg.package}/share/zsh-autoenv/autoenv.zsh + ''; + }; +} diff --git a/nixos/modules/programs/zsh/zsh-autosuggestions.nix b/nixos/modules/programs/zsh/zsh-autosuggestions.nix new file mode 100644 index 00000000000..2e53e907d54 --- /dev/null +++ b/nixos/modules/programs/zsh/zsh-autosuggestions.nix @@ -0,0 +1,73 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.zsh.autosuggestions; +in +{ + imports = [ + (mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [ "programs" "zsh" "autosuggestions" "enable" ]) + ]; + + options.programs.zsh.autosuggestions = { + + enable = mkEnableOption "zsh-autosuggestions"; + + highlightStyle = mkOption { + type = types.str; + default = "fg=8"; # https://github.com/zsh-users/zsh-autosuggestions/tree/v0.4.3#suggestion-highlight-style + description = "Highlight style for suggestions ({fore,back}ground color)"; + example = "fg=cyan"; + }; + + strategy = mkOption { + type = types.listOf (types.enum [ "history" "completion" "match_prev_cmd" ]); + default = [ "history" ]; + description = '' + `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. + The strategies in the array are tried successively until a suggestion is found. + There are currently three built-in strategies to choose from: + + - `history`: Chooses the most recent match from history. + - `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module) + - `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches + the most recently executed command. Note that this strategy won't work as expected with ZSH options that + don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`. + ''; + }; + + async = mkOption { + type = types.bool; + default = true; + description = "Whether to fetch suggestions asynchronously"; + example = false; + }; + + extraConfig = mkOption { + type = with types; attrsOf str; + default = {}; + description = "Attribute set with additional configuration values"; + example = literalExpression '' + { + "ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" = "20"; + } + ''; + }; + + }; + + config = mkIf cfg.enable { + + programs.zsh.interactiveShellInit = '' + source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh + + export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.highlightStyle}" + export ZSH_AUTOSUGGEST_STRATEGY=(${concatStringsSep " " cfg.strategy}) + ${optionalString (!cfg.async) "unset ZSH_AUTOSUGGEST_USE_ASYNC"} + + ${concatStringsSep "\n" (mapAttrsToList (key: value: ''export ${key}="${value}"'') cfg.extraConfig)} + ''; + + }; +} diff --git a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix new file mode 100644 index 00000000000..1eb53ccae52 --- /dev/null +++ b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix @@ -0,0 +1,107 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.zsh.syntaxHighlighting; +in +{ + imports = [ + (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ]) + (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "enable" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ]) + (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "highlighters" ] [ "programs" "zsh" "syntaxHighlighting" "highlighters" ]) + (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "patterns" ] [ "programs" "zsh" "syntaxHighlighting" "patterns" ]) + ]; + + options = { + programs.zsh.syntaxHighlighting = { + enable = mkEnableOption "zsh-syntax-highlighting"; + + highlighters = mkOption { + default = [ "main" ]; + + # https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md + type = types.listOf(types.enum([ + "main" + "brackets" + "pattern" + "cursor" + "root" + "line" + ])); + + description = '' + Specifies the highlighters to be used by zsh-syntax-highlighting. + + The following defined options can be found here: + https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md + ''; + }; + + patterns = mkOption { + default = {}; + type = types.attrsOf types.str; + + example = literalExpression '' + { + "rm -rf *" = "fg=white,bold,bg=red"; + } + ''; + + description = '' + Specifies custom patterns to be highlighted by zsh-syntax-highlighting. + + Please refer to the docs for more information about the usage: + https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/pattern.md + ''; + }; + styles = mkOption { + default = {}; + type = types.attrsOf types.str; + + example = literalExpression '' + { + "alias" = "fg=magenta,bold"; + } + ''; + + description = '' + Specifies custom styles to be highlighted by zsh-syntax-highlighting. + + Please refer to the docs for more information about the usage: + https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/main.md + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ zsh-syntax-highlighting ]; + + assertions = [ + { + assertion = length(attrNames cfg.patterns) > 0 -> elem "pattern" cfg.highlighters; + message = '' + When highlighting patterns, "pattern" needs to be included in the list of highlighters. + ''; + } + ]; + + programs.zsh.interactiveShellInit = with pkgs; + lib.mkAfter (lib.concatStringsSep "\n" ([ + "source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" + ] ++ optional (length(cfg.highlighters) > 0) + "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${concatStringsSep " " cfg.highlighters})" + ++ optionals (length(attrNames cfg.patterns) > 0) + (mapAttrsToList ( + pattern: design: + "ZSH_HIGHLIGHT_PATTERNS+=('${pattern}' '${design}')" + ) cfg.patterns) + ++ optionals (length(attrNames cfg.styles) > 0) + (mapAttrsToList ( + styles: design: + "ZSH_HIGHLIGHT_STYLES[${styles}]='${design}'" + ) cfg.styles) + )); + }; +} diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix new file mode 100644 index 00000000000..5fe98b6801b --- /dev/null +++ b/nixos/modules/programs/zsh/zsh.nix @@ -0,0 +1,303 @@ +# This module defines global configuration for the zshell. + +{ config, lib, options, pkgs, ... }: + +with lib; + +let + + cfge = config.environment; + + cfg = config.programs.zsh; + opt = options.programs.zsh; + + zshAliases = concatStringsSep "\n" ( + mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}") + (filterAttrs (k: v: v != null) cfg.shellAliases) + ); + + zshStartupNotes = '' + # Note that generated /etc/zprofile and /etc/zshrc files do a lot of + # non-standard setup to make zsh usable with no configuration by default. + # + # Which means that unless you explicitly meticulously override everything + # generated, interactions between your ~/.zshrc and these files are likely + # to be rather surprising. + # + # Note however, that you can disable loading of the generated /etc/zprofile + # and /etc/zshrc (you can't disable loading of /etc/zshenv, but it is + # designed to not set anything surprising) by setting `no_global_rcs` option + # in ~/.zshenv: + # + # echo setopt no_global_rcs >> ~/.zshenv + # + # See "STARTUP/SHUTDOWN FILES" section of zsh(1) for more info. + ''; + +in + +{ + + options = { + + programs.zsh = { + + enable = mkOption { + default = false; + description = '' + Whether to configure zsh as an interactive shell. To enable zsh for + a particular user, use the <option>users.users.<name?>.shell</option> + option for that user. To enable zsh system-wide use the + <option>users.defaultUserShell</option> option. + ''; + type = types.bool; + }; + + shellAliases = mkOption { + default = { }; + description = '' + Set of aliases for zsh shell, which overrides <option>environment.shellAliases</option>. + See <option>environment.shellAliases</option> for an option format description. + ''; + type = with types; attrsOf (nullOr (either str path)); + }; + + shellInit = mkOption { + default = ""; + description = '' + Shell script code called during zsh shell initialisation. + ''; + type = types.lines; + }; + + loginShellInit = mkOption { + default = ""; + description = '' + Shell script code called during zsh login shell initialisation. + ''; + type = types.lines; + }; + + interactiveShellInit = mkOption { + default = ""; + description = '' + Shell script code called during interactive zsh shell initialisation. + ''; + type = types.lines; + }; + + promptInit = mkOption { + default = '' + # Note that to manually override this in ~/.zshrc you should run `prompt off` + # before setting your PS1 and etc. Otherwise this will likely to interact with + # your ~/.zshrc configuration in unexpected ways as the default prompt sets + # a lot of different prompt variables. + autoload -U promptinit && promptinit && prompt suse && setopt prompt_sp + ''; + description = '' + Shell script code used to initialise the zsh prompt. + ''; + type = types.lines; + }; + + histSize = mkOption { + default = 2000; + description = '' + Change history size. + ''; + type = types.int; + }; + + histFile = mkOption { + default = "$HOME/.zsh_history"; + description = '' + Change history file. + ''; + type = types.str; + }; + + setOptions = mkOption { + type = types.listOf types.str; + default = [ + "HIST_IGNORE_DUPS" + "SHARE_HISTORY" + "HIST_FCNTL_LOCK" + ]; + example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ]; + description = '' + Configure zsh options. See + <citerefentry><refentrytitle>zshoptions</refentrytitle><manvolnum>1</manvolnum></citerefentry>. + ''; + }; + + enableCompletion = mkOption { + default = true; + description = '' + Enable zsh completion for all interactive zsh shells. + ''; + type = types.bool; + }; + + enableBashCompletion = mkOption { + default = false; + description = '' + Enable compatibility with bash's programmable completion system. + ''; + type = types.bool; + }; + + enableGlobalCompInit = mkOption { + default = cfg.enableCompletion; + defaultText = literalExpression "config.${opt.enableCompletion}"; + description = '' + Enable execution of compinit call for all interactive zsh shells. + + This option can be disabled if the user wants to extend its + <literal>fpath</literal> and a custom <literal>compinit</literal> + call in the local config is required. + ''; + type = types.bool; + }; + + }; + + }; + + config = mkIf cfg.enable { + + programs.zsh.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases; + + environment.etc.zshenv.text = + '' + # /etc/zshenv: DO NOT EDIT -- this file has been generated automatically. + # This file is read for all shells. + + # Only execute this file once per shell. + if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi + __ETC_ZSHENV_SOURCED=1 + + if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then + . ${config.system.build.setEnvironment} + fi + + HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help" + + # Tell zsh how to find installed completions. + for p in ''${(z)NIX_PROFILES}; do + fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions) + done + + # Setup custom shell init stuff. + ${cfge.shellInit} + + ${cfg.shellInit} + + # Read system-wide modifications. + if test -f /etc/zshenv.local; then + . /etc/zshenv.local + fi + ''; + + environment.etc.zprofile.text = + '' + # /etc/zprofile: DO NOT EDIT -- this file has been generated automatically. + # This file is read for login shells. + # + ${zshStartupNotes} + + # Only execute this file once per shell. + if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi + __ETC_ZPROFILE_SOURCED=1 + + # Setup custom login shell init stuff. + ${cfge.loginShellInit} + + ${cfg.loginShellInit} + + # Read system-wide modifications. + if test -f /etc/zprofile.local; then + . /etc/zprofile.local + fi + ''; + + environment.etc.zshrc.text = + '' + # /etc/zshrc: DO NOT EDIT -- this file has been generated automatically. + # This file is read for interactive shells. + # + ${zshStartupNotes} + + # Only execute this file once per shell. + if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi + __ETC_ZSHRC_SOURCED=1 + + ${optionalString (cfg.setOptions != []) '' + # Set zsh options. + setopt ${concatStringsSep " " cfg.setOptions} + ''} + + # Setup command line history. + # Don't export these, otherwise other shells (bash) will try to use same HISTFILE. + SAVEHIST=${toString cfg.histSize} + HISTSIZE=${toString cfg.histSize} + HISTFILE=${cfg.histFile} + + # Configure sane keyboard defaults. + . /etc/zinputrc + + ${optionalString cfg.enableGlobalCompInit '' + # Enable autocompletion. + autoload -U compinit && compinit + ''} + + ${optionalString cfg.enableBashCompletion '' + # Enable compatibility with bash's completion system. + autoload -U bashcompinit && bashcompinit + ''} + + # Setup custom interactive shell init stuff. + ${cfge.interactiveShellInit} + + ${cfg.interactiveShellInit} + + # Setup aliases. + ${zshAliases} + + # Setup prompt. + ${cfg.promptInit} + + # Disable some features to support TRAMP. + if [ "$TERM" = dumb ]; then + unsetopt zle prompt_cr prompt_subst + unset RPS1 RPROMPT + PS1='$ ' + PROMPT='$ ' + fi + + # Read system-wide modifications. + if test -f /etc/zshrc.local; then + . /etc/zshrc.local + fi + ''; + + # Bug in nix flakes: + # If we use `.source` here the path is garbage collected also we point to it with a symlink + # see https://github.com/NixOS/nixpkgs/issues/132732 + environment.etc.zinputrc.text = builtins.readFile ./zinputrc; + + environment.systemPackages = [ pkgs.zsh ] + ++ optional cfg.enableCompletion pkgs.nix-zsh-completions; + + environment.pathsToLink = optional cfg.enableCompletion "/share/zsh"; + + #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh"; + + environment.shells = + [ + "/run/current-system/sw/bin/zsh" + "${pkgs.zsh}/bin/zsh" + ]; + + }; + +} |