at 18.09-beta 12 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 inherit (pkgs) cups cups-pk-helper cups-filters; 8 9 cfg = config.services.printing; 10 11 avahiEnabled = config.services.avahi.enable; 12 polkitEnabled = config.security.polkit.enable; 13 14 additionalBackends = pkgs.runCommand "additional-cups-backends" { } 15 '' 16 mkdir -p $out 17 if [ ! -e ${cups.out}/lib/cups/backend/smb ]; then 18 mkdir -p $out/lib/cups/backend 19 ln -sv ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb 20 fi 21 22 # Provide support for printing via HTTPS. 23 if [ ! -e ${cups.out}/lib/cups/backend/https ]; then 24 mkdir -p $out/lib/cups/backend 25 ln -sv ${cups.out}/lib/cups/backend/ipp $out/lib/cups/backend/https 26 fi 27 ''; 28 29 # Here we can enable additional backends, filters, etc. that are not 30 # part of CUPS itself, e.g. the SMB backend is part of Samba. Since 31 # we can't update ${cups.out}/lib/cups itself, we create a symlink tree 32 # here and add the additional programs. The ServerBin directive in 33 # cupsd.conf tells cupsd to use this tree. 34 bindir = pkgs.buildEnv { 35 name = "cups-progs"; 36 paths = 37 [ cups.out additionalBackends cups-filters pkgs.ghostscript ] 38 ++ cfg.drivers; 39 pathsToLink = [ "/lib" "/share/cups" "/bin" ]; 40 postBuild = cfg.bindirCmds; 41 ignoreCollisions = true; 42 }; 43 44 writeConf = name: text: pkgs.writeTextFile { 45 inherit name text; 46 destination = "/etc/cups/${name}"; 47 }; 48 49 cupsFilesFile = writeConf "cups-files.conf" '' 50 SystemGroup root wheel 51 52 ServerBin ${bindir}/lib/cups 53 DataDir ${bindir}/share/cups 54 DocumentRoot ${cups.out}/share/doc/cups 55 56 AccessLog syslog 57 ErrorLog syslog 58 PageLog syslog 59 60 TempDir ${cfg.tempDir} 61 62 # User and group used to run external programs, including 63 # those that actually send the job to the printer. Note that 64 # Udev sets the group of printer devices to `lp', so we want 65 # these programs to run as `lp' as well. 66 User cups 67 Group lp 68 69 ${cfg.extraFilesConf} 70 ''; 71 72 cupsdFile = writeConf "cupsd.conf" '' 73 ${concatMapStrings (addr: '' 74 Listen ${addr} 75 '') cfg.listenAddresses} 76 Listen /var/run/cups/cups.sock 77 78 SetEnv PATH /var/lib/cups/path/lib/cups/filter:/var/lib/cups/path/bin 79 80 DefaultShared ${if cfg.defaultShared then "Yes" else "No"} 81 82 Browsing ${if cfg.browsing then "Yes" else "No"} 83 84 WebInterface ${if cfg.webInterface then "Yes" else "No"} 85 86 LogLevel ${cfg.logLevel} 87 88 ${cfg.extraConf} 89 ''; 90 91 browsedFile = writeConf "cups-browsed.conf" cfg.browsedConf; 92 93 rootdir = pkgs.buildEnv { 94 name = "cups-progs"; 95 paths = [ 96 cupsFilesFile 97 cupsdFile 98 (writeConf "client.conf" cfg.clientConf) 99 (writeConf "snmp.conf" cfg.snmpConf) 100 ] ++ optional avahiEnabled browsedFile 101 ++ cfg.drivers; 102 pathsToLink = [ "/etc/cups" ]; 103 ignoreCollisions = true; 104 }; 105 106 filterGutenprint = pkgs: filter (pkg: pkg.meta.isGutenprint or false == true) pkgs; 107 containsGutenprint = pkgs: length (filterGutenprint pkgs) > 0; 108 getGutenprint = pkgs: head (filterGutenprint pkgs); 109 110in 111 112{ 113 114 ###### interface 115 116 options = { 117 services.printing = { 118 119 enable = mkOption { 120 type = types.bool; 121 default = false; 122 description = '' 123 Whether to enable printing support through the CUPS daemon. 124 ''; 125 }; 126 127 startWhenNeeded = mkOption { 128 type = types.bool; 129 default = false; 130 description = '' 131 If set, CUPS is socket-activated; that is, 132 instead of having it permanently running as a daemon, 133 systemd will start it on the first incoming connection. 134 ''; 135 }; 136 137 listenAddresses = mkOption { 138 type = types.listOf types.str; 139 default = [ "localhost:631" ]; 140 example = [ "*:631" ]; 141 description = '' 142 A list of addresses and ports on which to listen. 143 ''; 144 }; 145 146 bindirCmds = mkOption { 147 type = types.lines; 148 internal = true; 149 default = ""; 150 description = '' 151 Additional commands executed while creating the directory 152 containing the CUPS server binaries. 153 ''; 154 }; 155 156 defaultShared = mkOption { 157 type = types.bool; 158 default = false; 159 description = '' 160 Specifies whether local printers are shared by default. 161 ''; 162 }; 163 164 browsing = mkOption { 165 type = types.bool; 166 default = false; 167 description = '' 168 Specifies whether shared printers are advertised. 169 ''; 170 }; 171 172 webInterface = mkOption { 173 type = types.bool; 174 default = true; 175 description = '' 176 Specifies whether the web interface is enabled. 177 ''; 178 }; 179 180 logLevel = mkOption { 181 type = types.str; 182 default = "info"; 183 example = "debug"; 184 description = '' 185 Specifies the cupsd logging verbosity. 186 ''; 187 }; 188 189 extraFilesConf = mkOption { 190 type = types.lines; 191 default = ""; 192 description = '' 193 Extra contents of the configuration file of the CUPS daemon 194 (<filename>cups-files.conf</filename>). 195 ''; 196 }; 197 198 extraConf = mkOption { 199 type = types.lines; 200 default = ""; 201 example = 202 '' 203 BrowsePoll cups.example.com 204 MaxCopies 42 205 ''; 206 description = '' 207 Extra contents of the configuration file of the CUPS daemon 208 (<filename>cupsd.conf</filename>). 209 ''; 210 }; 211 212 clientConf = mkOption { 213 type = types.lines; 214 default = ""; 215 example = 216 '' 217 ServerName server.example.com 218 Encryption Never 219 ''; 220 description = '' 221 The contents of the client configuration. 222 (<filename>client.conf</filename>) 223 ''; 224 }; 225 226 browsedConf = mkOption { 227 type = types.lines; 228 default = ""; 229 example = 230 '' 231 BrowsePoll cups.example.com 232 ''; 233 description = '' 234 The contents of the configuration. file of the CUPS Browsed daemon 235 (<filename>cups-browsed.conf</filename>) 236 ''; 237 }; 238 239 snmpConf = mkOption { 240 type = types.lines; 241 default = '' 242 Address @LOCAL 243 ''; 244 description = '' 245 The contents of <filename>/etc/cups/snmp.conf</filename>. See "man 246 cups-snmp.conf" for a complete description. 247 ''; 248 }; 249 250 drivers = mkOption { 251 type = types.listOf types.path; 252 default = []; 253 example = literalExample "[ pkgs.gutenprint pkgs.hplip pkgs.splix ]"; 254 description = '' 255 CUPS drivers to use. Drivers provided by CUPS, cups-filters, 256 Ghostscript and Samba are added unconditionally. If this list contains 257 Gutenprint (i.e. a derivation with 258 <literal>meta.isGutenprint = true</literal>) the PPD files in 259 <filename>/var/lib/cups/ppd</filename> will be updated automatically 260 to avoid errors due to incompatible versions. 261 ''; 262 }; 263 264 tempDir = mkOption { 265 type = types.path; 266 default = "/tmp"; 267 example = "/tmp/cups"; 268 description = '' 269 CUPSd temporary directory. 270 ''; 271 }; 272 }; 273 274 }; 275 276 277 ###### implementation 278 279 config = mkIf config.services.printing.enable { 280 281 users.users = singleton 282 { name = "cups"; 283 uid = config.ids.uids.cups; 284 group = "lp"; 285 description = "CUPS printing services"; 286 }; 287 288 environment.systemPackages = [ cups.out ] ++ optional polkitEnabled cups-pk-helper; 289 environment.etc."cups".source = "/var/lib/cups"; 290 291 services.dbus.packages = [ cups.out ] ++ optional polkitEnabled cups-pk-helper; 292 293 # Cups uses libusb to talk to printers, and does not use the 294 # linux kernel driver. If the driver is not in a black list, it 295 # gets loaded, and then cups cannot access the printers. 296 boot.blacklistedKernelModules = [ "usblp" ]; 297 298 systemd.packages = [ cups.out ]; 299 300 systemd.sockets.cups = mkIf cfg.startWhenNeeded { 301 wantedBy = [ "sockets.target" ]; 302 listenStreams = map (x: replaceStrings ["localhost"] ["127.0.0.1"] (removePrefix "*:" x)) cfg.listenAddresses; 303 }; 304 305 systemd.services.cups = 306 { wantedBy = optionals (!cfg.startWhenNeeded) [ "multi-user.target" ]; 307 wants = [ "network.target" ]; 308 after = [ "network.target" ]; 309 310 path = [ cups.out ]; 311 312 preStart = 313 '' 314 mkdir -m 0700 -p /var/cache/cups 315 mkdir -m 0700 -p /var/spool/cups 316 mkdir -m 0755 -p ${cfg.tempDir} 317 318 mkdir -m 0755 -p /var/lib/cups 319 # Backwards compatibility 320 if [ ! -L /etc/cups ]; then 321 mv /etc/cups/* /var/lib/cups 322 rmdir /etc/cups 323 ln -s /var/lib/cups /etc/cups 324 fi 325 # First, clean existing symlinks 326 if [ -n "$(ls /var/lib/cups)" ]; then 327 for i in /var/lib/cups/*; do 328 [ -L "$i" ] && rm "$i" 329 done 330 fi 331 # Then, populate it with static files 332 cd ${rootdir}/etc/cups 333 for i in *; do 334 [ ! -e "/var/lib/cups/$i" ] && ln -s "${rootdir}/etc/cups/$i" "/var/lib/cups/$i" 335 done 336 337 #update path reference 338 [ -L /var/lib/cups/path ] && \ 339 rm /var/lib/cups/path 340 [ ! -e /var/lib/cups/path ] && \ 341 ln -s ${bindir} /var/lib/cups/path 342 343 ${optionalString (containsGutenprint cfg.drivers) '' 344 if [ -d /var/lib/cups/ppd ]; then 345 ${getGutenprint cfg.drivers}/bin/cups-genppdupdate -p /var/lib/cups/ppd 346 fi 347 ''} 348 ''; 349 350 serviceConfig = { 351 PrivateTmp = true; 352 RuntimeDirectory = [ "cups" ]; 353 }; 354 }; 355 356 systemd.services.cups-browsed = mkIf avahiEnabled 357 { description = "CUPS Remote Printer Discovery"; 358 359 wantedBy = [ "multi-user.target" ]; 360 wants = [ "cups.service" "avahi-daemon.service" ]; 361 bindsTo = [ "cups.service" "avahi-daemon.service" ]; 362 partOf = [ "cups.service" "avahi-daemon.service" ]; 363 after = [ "cups.service" "avahi-daemon.service" ]; 364 365 path = [ cups ]; 366 367 serviceConfig.ExecStart = "${cups-filters}/bin/cups-browsed"; 368 369 restartTriggers = [ browsedFile ]; 370 }; 371 372 services.printing.extraConf = 373 '' 374 DefaultAuthType Basic 375 376 <Location /> 377 Order allow,deny 378 Allow localhost 379 </Location> 380 381 <Location /admin> 382 Order allow,deny 383 Allow localhost 384 </Location> 385 386 <Location /admin/conf> 387 AuthType Basic 388 Require user @SYSTEM 389 Order allow,deny 390 Allow localhost 391 </Location> 392 393 <Policy default> 394 <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job CUPS-Move-Job> 395 Require user @OWNER @SYSTEM 396 Order deny,allow 397 </Limit> 398 399 <Limit Pause-Printer Resume-Printer Set-Printer-Attributes Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After CUPS-Add-Printer CUPS-Delete-Printer CUPS-Add-Class CUPS-Delete-Class CUPS-Accept-Jobs CUPS-Reject-Jobs CUPS-Set-Default> 400 AuthType Basic 401 Require user @SYSTEM 402 Order deny,allow 403 </Limit> 404 405 <Limit Cancel-Job CUPS-Authenticate-Job> 406 Require user @OWNER @SYSTEM 407 Order deny,allow 408 </Limit> 409 410 <Limit All> 411 Order deny,allow 412 </Limit> 413 </Policy> 414 ''; 415 416 security.pam.services.cups = {}; 417 418 }; 419}