at 25.11-pre 6.1 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8let 9 cfg = config.hardware.bluetooth; 10 package = cfg.package; 11 12 inherit (lib) 13 mkEnableOption 14 mkIf 15 mkOption 16 mkPackageOption 17 mkRenamedOptionModule 18 mkRemovedOptionModule 19 concatStringsSep 20 optional 21 optionalAttrs 22 recursiveUpdate 23 types 24 ; 25 26 cfgFmt = pkgs.formats.ini { }; 27 28 defaults = { 29 General.ControllerMode = "dual"; 30 Policy.AutoEnable = cfg.powerOnBoot; 31 }; 32 33 hasDisabledPlugins = builtins.length cfg.disabledPlugins > 0; 34 35in 36{ 37 imports = [ 38 (mkRenamedOptionModule [ "hardware" "bluetooth" "config" ] [ "hardware" "bluetooth" "settings" ]) 39 (mkRemovedOptionModule [ "hardware" "bluetooth" "extraConfig" ] '' 40 Use hardware.bluetooth.settings instead. 41 42 This is part of the general move to use structured settings instead of raw 43 text for config as introduced by RFC0042: 44 https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md 45 '') 46 ]; 47 48 ###### interface 49 50 options = { 51 52 hardware.bluetooth = { 53 enable = mkEnableOption "support for Bluetooth"; 54 55 hsphfpd.enable = mkEnableOption "support for hsphfpd[-prototype] implementation"; 56 57 powerOnBoot = mkOption { 58 type = types.bool; 59 default = true; 60 description = "Whether to power up the default Bluetooth controller on boot."; 61 }; 62 63 package = mkPackageOption pkgs "bluez" { }; 64 65 disabledPlugins = mkOption { 66 type = types.listOf types.str; 67 default = [ ]; 68 description = "Built-in plugins to disable"; 69 }; 70 71 settings = mkOption { 72 type = cfgFmt.type; 73 default = { }; 74 example = { 75 General = { 76 ControllerMode = "bredr"; 77 }; 78 }; 79 description = '' 80 Set configuration for system-wide bluetooth (/etc/bluetooth/main.conf). 81 See <https://github.com/bluez/bluez/blob/master/src/main.conf> for full list of options. 82 ''; 83 }; 84 85 input = mkOption { 86 type = cfgFmt.type; 87 default = { }; 88 example = { 89 General = { 90 IdleTimeout = 30; 91 ClassicBondedOnly = true; 92 }; 93 }; 94 description = '' 95 Set configuration for the input service (/etc/bluetooth/input.conf). 96 See <https://github.com/bluez/bluez/blob/master/profiles/input/input.conf> for full list of options. 97 ''; 98 }; 99 100 network = mkOption { 101 type = cfgFmt.type; 102 default = { }; 103 example = { 104 General = { 105 DisableSecurity = true; 106 }; 107 }; 108 description = '' 109 Set configuration for the network service (/etc/bluetooth/network.conf). 110 See <https://github.com/bluez/bluez/blob/master/profiles/network/network.conf> for full list of options. 111 ''; 112 }; 113 }; 114 }; 115 116 ###### implementation 117 118 config = mkIf cfg.enable { 119 environment.systemPackages = [ package ] ++ optional cfg.hsphfpd.enable pkgs.hsphfpd; 120 121 environment.etc."bluetooth/input.conf".source = cfgFmt.generate "input.conf" cfg.input; 122 environment.etc."bluetooth/network.conf".source = cfgFmt.generate "network.conf" cfg.network; 123 environment.etc."bluetooth/main.conf".source = cfgFmt.generate "main.conf" ( 124 recursiveUpdate defaults cfg.settings 125 ); 126 services.udev.packages = [ package ]; 127 services.dbus.packages = [ package ] ++ optional cfg.hsphfpd.enable pkgs.hsphfpd; 128 systemd.packages = [ package ]; 129 130 systemd.services = 131 { 132 bluetooth = 133 let 134 # `man bluetoothd` will refer to main.conf in the nix store but bluez 135 # will in fact load the configuration file at /etc/bluetooth/main.conf 136 # so force it here to avoid any ambiguity and things suddenly breaking 137 # if/when the bluez derivation is changed. 138 args = [ 139 "-f" 140 "/etc/bluetooth/main.conf" 141 ] ++ optional hasDisabledPlugins "--noplugin=${concatStringsSep "," cfg.disabledPlugins}"; 142 in 143 { 144 wantedBy = [ "bluetooth.target" ]; 145 aliases = [ "dbus-org.bluez.service" ]; 146 # restarting can leave people without a mouse/keyboard 147 restartIfChanged = false; 148 serviceConfig = { 149 ExecStart = [ 150 "" 151 "${package}/libexec/bluetooth/bluetoothd ${utils.escapeSystemdExecArgs args}" 152 ]; 153 CapabilityBoundingSet = [ 154 "CAP_NET_BIND_SERVICE" # sockets and tethering 155 ]; 156 ConfigurationDirectoryMode = "0755"; 157 NoNewPrivileges = true; 158 RestrictNamespaces = true; 159 ProtectControlGroups = true; 160 MemoryDenyWriteExecute = true; 161 RestrictSUIDSGID = true; 162 SystemCallArchitectures = "native"; 163 SystemCallFilter = "@system-service"; 164 LockPersonality = true; 165 RestrictRealtime = true; 166 ProtectProc = "invisible"; 167 PrivateTmp = true; 168 169 PrivateUsers = false; 170 171 # loading hardware modules 172 ProtectKernelModules = false; 173 ProtectKernelTunables = false; 174 175 PrivateNetwork = false; # tethering 176 }; 177 }; 178 } 179 // (optionalAttrs cfg.hsphfpd.enable { 180 hsphfpd = { 181 after = [ "bluetooth.service" ]; 182 requires = [ "bluetooth.service" ]; 183 wantedBy = [ "bluetooth.target" ]; 184 185 description = "A prototype implementation used for connecting HSP/HFP Bluetooth devices"; 186 serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/hsphfpd.pl"; 187 }; 188 }); 189 190 systemd.user.services = 191 { 192 obex.aliases = [ "dbus-org.bluez.obex.service" ]; 193 } 194 // optionalAttrs cfg.hsphfpd.enable { 195 telephony_client = { 196 wantedBy = [ "default.target" ]; 197 198 description = "telephony_client for hsphfpd"; 199 serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/telephony_client.pl"; 200 }; 201 }; 202 }; 203}