diff options
Diffstat (limited to 'nixos/modules/programs/bash')
-rw-r--r-- | nixos/modules/programs/bash/bash.nix | 217 | ||||
-rw-r--r-- | nixos/modules/programs/bash/command-not-found.nix | 51 | ||||
-rw-r--r-- | nixos/modules/programs/bash/command-not-found.pl | 48 | ||||
-rw-r--r-- | nixos/modules/programs/bash/inputrc | 36 |
4 files changed, 352 insertions, 0 deletions
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix new file mode 100644 index 00000000000..8cfe3f990ad --- /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, pkgs, ... }: + +with pkgs.lib; + +let + + cfge = config.environment; + + cfg = config.programs.bash; + + bashCompletion = optionalString cfg.enableCompletion '' + # 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). + if shopt -q progcomp &>/dev/null; then + . "${pkgs.bashCompletion}/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/"* "$p/share/bash-completion/completions/"*; do + . $m + done + done + eval "$nullglobStatus" + unset nullglobStatus p m + fi + ''; + + bashAliases = concatStringsSep "\n" ( + mapAttrsFlatten (k: v: "alias ${k}='${v}'") cfg.shellAliases + ); + +in + +{ + 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 = config.environment.shellAliases // { which = "type -P"; }; + description = '' + Set of aliases for bash shell. See <option>environment.shellAliases</option> + for an option format description. + ''; + type = types.attrs; # types.attrsOf types.stringOrPath; + }; + + 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. + PROMPT_COLOR="1;31m" + let $UID && PROMPT_COLOR="1;32m" + PS1="\n\[\033[$PROMPT_COLOR\][\u@\h:\w]\\$\[\033[0m\] " + if test "$TERM" = "xterm"; then + PS1="\[\033]2;\h:\u:\w\007\]$PS1" + fi + ''; + description = '' + Shell script code used to initialise the bash prompt. + ''; + type = types.lines; + }; + + enableCompletion = mkOption { + default = false; + description = '' + Enable Bash completion for all interactive bash shells. + ''; + type = types.bool; + }; + + }; + + }; + + config = mkIf cfg.enable { + + programs.bash = { + + shellInit = '' + . ${config.system.build.setEnvironment} + + ${cfge.shellInit} + ''; + + loginShellInit = cfge.loginShellInit; + + interactiveShellInit = '' + ${cfge.interactiveShellInit} + + # Check the window size after every command. + shopt -s checkwinsize + + # Disable hashing (i.e. caching) of command lookups. + set +h + + ${cfg.promptInit} + ${bashCompletion} + ${bashAliases} + ''; + + }; + + 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" -o -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. + environment.etc."inputrc".source = ./inputrc; + + users.defaultUserShell = mkDefault "/run/current-system/sw/bin/bash"; + + environment.pathsToLink = optionals cfg.enableCompletion [ + "/etc/bash_completion.d" + "/share/bash-completion" + ]; + + environment.shells = + [ "/run/current-system/sw/bin/bash" + "/var/run/current-system/sw/bin/bash" + "/run/current-system/sw/bin/sh" + "/var/run/current-system/sw/bin/sh" + "${pkgs.bashInteractive}/bin/bash" + "${pkgs.bashInteractive}/bin/sh" + ]; + + }; + +} diff --git a/nixos/modules/programs/bash/command-not-found.nix b/nixos/modules/programs/bash/command-not-found.nix new file mode 100644 index 00000000000..502320446a3 --- /dev/null +++ b/nixos/modules/programs/bash/command-not-found.nix @@ -0,0 +1,51 @@ +# 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, pkgs, ... }: + +with pkgs.lib; + +let + + commandNotFound = pkgs.substituteAll { + name = "command-not-found"; + dir = "bin"; + src = ./command-not-found.pl; + isExecutable = true; + inherit (pkgs) perl; + perlFlags = concatStrings (map (path: "-I ${path}/lib/perl5/site_perl ") + [ pkgs.perlPackages.DBI pkgs.perlPackages.DBDSQLite ]); + }; + +in + +{ + + programs.bash.interactiveShellInit = + '' + # This function is called whenever a command is not found. + command_not_found_handle() { + local p=/run/current-system/sw/bin/command-not-found + if [ -x $p -a -f /nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite ]; then + # Run the helper program. + $p "$1" + # 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 + } + ''; + + environment.systemPackages = [ commandNotFound ]; + + # TODO: tab completion for uninstalled commands! :-) + +} diff --git a/nixos/modules/programs/bash/command-not-found.pl b/nixos/modules/programs/bash/command-not-found.pl new file mode 100644 index 00000000000..916649059d3 --- /dev/null +++ b/nixos/modules/programs/bash/command-not-found.pl @@ -0,0 +1,48 @@ +#! @perl@/bin/perl -w @perlFlags@ + +use strict; +use DBI; +use DBD::SQLite; +use Config; + +my $program = $ARGV[0]; + +my $dbPath = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite"; + +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); + +if (!defined $res || scalar @$res == 0) { + print STDERR "$program: command not found\n"; +} elsif (scalar @$res == 1) { + my $package = @$res[0]->{package}; + if ($ENV{"NIX_AUTO_INSTALL"} // "") { + print STDERR <<EOF; +The program ‘$program’ is currently not installed. It is provided by +the package ‘$package’, which I will now install for you. +EOF + ; + exit 126 if system("nix-env", "-i", $package) == 0; + } else { + print STDERR <<EOF; +The program ‘$program’ is currently not installed. You can install it by typing: + nix-env -i $package +EOF + } +} else { + print STDERR <<EOF; +The program ‘$program’ is currently not installed. It is provided by +several packages. You can install it by typing one of the following: +EOF + print STDERR " nix-env -i $_->{package}\n" foreach @$res; +} + +exit 127; diff --git a/nixos/modules/programs/bash/inputrc b/nixos/modules/programs/bash/inputrc new file mode 100644 index 00000000000..e4eabc052c5 --- /dev/null +++ b/nixos/modules/programs/bash/inputrc @@ -0,0 +1,36 @@ +# 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 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 |