at 24.11-pre 6.9 kB view raw
1{ config, lib, pkgs, ... }: 2 3let 4 cfg = config.programs.dconf; 5 6 # Compile keyfiles to dconf DB 7 compileDconfDb = dir: pkgs.runCommand "dconf-db" 8 { 9 nativeBuildInputs = [ (lib.getBin pkgs.dconf) ]; 10 } "dconf compile $out ${dir}"; 11 12 # Check if dconf keyfiles are valid 13 checkDconfKeyfiles = dir: pkgs.runCommand "check-dconf-keyfiles" 14 { 15 nativeBuildInputs = [ (lib.getBin pkgs.dconf) ]; 16 } '' 17 if [[ -f ${dir} ]]; then 18 echo "dconf keyfiles should be a directory but a file is provided: ${dir}" 19 exit 1 20 fi 21 22 dconf compile db ${dir} || ( 23 echo "The dconf keyfiles are invalid: ${dir}" 24 exit 1 25 ) 26 cp -R ${dir} $out 27 ''; 28 29 mkAllLocks = settings: lib.flatten ( 30 lib.mapAttrsToList (k: v: lib.mapAttrsToList (k': _: "/${k}/${k'}") v) settings); 31 32 # Generate dconf DB from dconfDatabase and keyfiles 33 mkDconfDb = val: compileDconfDb (pkgs.symlinkJoin { 34 name = "nixos-generated-dconf-keyfiles"; 35 paths = [ 36 (pkgs.writeTextDir "nixos-generated-dconf-keyfiles" (lib.generators.toDconfINI val.settings)) 37 (pkgs.writeTextDir "locks/nixos-generated-dconf-locks" (lib.concatStringsSep "\n" 38 (if val.lockAll then mkAllLocks val.settings else val.locks) 39 )) 40 ] ++ (map checkDconfKeyfiles val.keyfiles); 41 }); 42 43 # Check if a dconf DB file is valid. The dconf cli doesn't return 1 when it can't 44 # open the database file so we have to check if the output is empty. 45 checkDconfDb = file: pkgs.runCommand "check-dconf-db" 46 { 47 nativeBuildInputs = [ (lib.getBin pkgs.dconf) ]; 48 } '' 49 if [[ -d ${file} ]]; then 50 echo "dconf DB should be a file but a directory is provided: ${file}" 51 exit 1 52 fi 53 54 echo "file-db:${file}" > profile 55 DCONF_PROFILE=$(pwd)/profile dconf dump / > output 2> error 56 if [[ ! -s output ]] && [[ -s error ]]; then 57 cat error 58 echo "The dconf DB file is invalid: ${file}" 59 exit 1 60 fi 61 62 cp ${file} $out 63 ''; 64 65 # Generate dconf profile 66 mkDconfProfile = name: value: 67 if lib.isDerivation value || lib.isPath value then 68 pkgs.runCommand "dconf-profile" { } '' 69 if [[ -d ${value} ]]; then 70 echo "Dconf profile should be a file but a directory is provided." 71 exit 1 72 fi 73 mkdir -p $out/etc/dconf/profile/ 74 cp ${value} $out/etc/dconf/profile/${name} 75 '' 76 else 77 pkgs.writeTextDir "etc/dconf/profile/${name}" ( 78 lib.concatMapStrings (x: "${x}\n") (( 79 lib.optional value.enableUserDb "user-db:user" 80 ) ++ ( 81 map 82 (value: 83 let 84 db = if lib.isAttrs value && !lib.isDerivation value then mkDconfDb value else checkDconfDb value; 85 in 86 "file-db:${db}") 87 value.databases 88 )) 89 ); 90 91 dconfDatabase = with lib.types; submodule { 92 options = { 93 keyfiles = lib.mkOption { 94 type = listOf (oneOf [ path package ]); 95 default = [ ]; 96 description = "A list of dconf keyfile directories."; 97 }; 98 settings = lib.mkOption { 99 type = attrs; 100 default = { }; 101 description = "An attrset used to generate dconf keyfile."; 102 example = literalExpression '' 103 with lib.gvariant; 104 { 105 "com/raggesilver/BlackBox" = { 106 scrollback-lines = mkUint32 10000; 107 theme-dark = "Tommorow Night"; 108 }; 109 } 110 ''; 111 }; 112 locks = lib.mkOption { 113 type = with lib.types; listOf str; 114 default = [ ]; 115 description = '' 116 A list of dconf keys to be lockdown. This doesn't take effect if `lockAll` 117 is set. 118 ''; 119 example = literalExpression '' 120 [ "/org/gnome/desktop/background/picture-uri" ] 121 ''; 122 }; 123 lockAll = lib.mkOption { 124 type = lib.types.bool; 125 default = false; 126 description = "Lockdown all dconf keys in `settings`."; 127 }; 128 }; 129 }; 130 131 dconfProfile = with lib.types; submodule { 132 options = { 133 enableUserDb = lib.mkOption { 134 type = bool; 135 default = true; 136 description = "Add `user-db:user` at the beginning of the profile."; 137 }; 138 139 databases = lib.mkOption { 140 type = with lib.types; listOf (oneOf [ 141 path 142 package 143 dconfDatabase 144 ]); 145 default = [ ]; 146 description = '' 147 List of data sources for the profile. An element can be an attrset, 148 or the path of an already compiled database. Each element is converted 149 to a file-db. 150 151 A key is searched from up to down and the first result takes the 152 priority. If a lock for a particular key is installed then the value from 153 the last database in the profile where the key is locked will be used. 154 This can be used to enforce mandatory settings. 155 ''; 156 }; 157 }; 158 }; 159 160in 161{ 162 options = { 163 programs.dconf = { 164 enable = lib.mkEnableOption "dconf"; 165 166 profiles = lib.mkOption { 167 type = with lib.types; attrsOf (oneOf [ 168 path 169 package 170 dconfProfile 171 ]); 172 default = { }; 173 description = '' 174 Attrset of dconf profiles. By default the `user` profile is used which 175 ends up in `/etc/dconf/profile/user`. 176 ''; 177 example = lib.literalExpression '' 178 { 179 # A "user" profile with a database 180 user.databases = [ 181 { 182 settings = { }; 183 } 184 ]; 185 # A "bar" profile from a package 186 bar = pkgs.bar-dconf-profile; 187 # A "foo" profile from a path 188 foo = ''${./foo} 189 }; 190 ''; 191 }; 192 193 packages = lib.mkOption { 194 type = lib.types.listOf lib.types.package; 195 default = [ ]; 196 description = "A list of packages which provide dconf profiles and databases in {file}`/etc/dconf`."; 197 }; 198 }; 199 }; 200 201 config = lib.mkIf (cfg.profiles != { } || cfg.enable) { 202 programs.dconf.packages = lib.mapAttrsToList mkDconfProfile cfg.profiles; 203 204 environment.etc.dconf = lib.mkIf (cfg.packages != [ ]) { 205 source = pkgs.symlinkJoin { 206 name = "dconf-system-config"; 207 paths = map (x: "${x}/etc/dconf") cfg.packages; 208 nativeBuildInputs = [ (lib.getBin pkgs.dconf) ]; 209 postBuild = '' 210 if test -d $out/db; then 211 dconf update $out/db 212 fi 213 ''; 214 }; 215 }; 216 217 services.dbus.packages = [ pkgs.dconf ]; 218 219 systemd.packages = [ pkgs.dconf ]; 220 221 # For dconf executable 222 environment.systemPackages = [ pkgs.dconf ]; 223 224 environment.sessionVariables = lib.mkIf cfg.enable { 225 # Needed for unwrapped applications 226 GIO_EXTRA_MODULES = [ "${pkgs.dconf.lib}/lib/gio/modules" ]; 227 }; 228 }; 229}