3
0
Fork 0
forked from mirrors/nixpkgs

Merge pull request #203454 from rnhmjoj/pr-cups-socket

nixos/hardware/printers: stop cupsd when unneeded
This commit is contained in:
Michele Guerini Rocco 2022-12-21 23:06:00 +01:00 committed by GitHub
commit 5dff7733aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 106 deletions

View file

@ -110,21 +110,26 @@ in {
};
config = mkIf (cfg.ensurePrinters != [] && config.services.printing.enable) {
systemd.services.ensure-printers = let
cupsUnit = if config.services.printing.startWhenNeeded then "cups.socket" else "cups.service";
in {
systemd.services.ensure-printers = {
description = "Ensure NixOS-configured CUPS printers";
wantedBy = [ "multi-user.target" ];
requires = [ cupsUnit ];
after = [ cupsUnit ];
wants = [ "cups.service" ];
after = [ "cups.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = concatMapStringsSep "\n" ensurePrinter cfg.ensurePrinters
+ optionalString (cfg.ensureDefaultPrinter != null) (ensureDefaultPrinter cfg.ensureDefaultPrinter);
script = concatStringsSep "\n" [
(concatMapStrings ensurePrinter cfg.ensurePrinters)
(optionalString (cfg.ensureDefaultPrinter != null)
(ensureDefaultPrinter cfg.ensureDefaultPrinter))
# Note: if cupsd is "stateless" the service can't be stopped,
# otherwise the configuration will be wiped on the next start.
(optionalString (with config.services.printing; startWhenNeeded && !stateless)
"systemctl stop cups.service")
];
};
};
}

View file

@ -341,7 +341,7 @@ in
systemd.sockets.cups = mkIf cfg.startWhenNeeded {
wantedBy = [ "sockets.target" ];
listenStreams = [ "/run/cups/cups.sock" ]
listenStreams = [ "" "/run/cups/cups.sock" ]
++ map (x: replaceStrings ["localhost"] ["127.0.0.1"] (removePrefix "*:" x)) cfg.listenAddresses;
};
@ -395,10 +395,7 @@ in
''}
'';
serviceConfig = {
PrivateTmp = true;
RuntimeDirectory = [ "cups" ];
};
serviceConfig.PrivateTmp = true;
};
systemd.services.cups-browsed = mkIf avahiEnabled

View file

@ -146,7 +146,8 @@ in rec {
(onFullSupported "nixos.tests.predictable-interface-names.predictable")
(onFullSupported "nixos.tests.predictable-interface-names.unpredictableNetworkd")
(onFullSupported "nixos.tests.predictable-interface-names.unpredictable")
(onFullSupported "nixos.tests.printing")
(onFullSupported "nixos.tests.printing-service")
(onFullSupported "nixos.tests.printing-socket")
(onFullSupported "nixos.tests.proxy")
(onFullSupported "nixos.tests.sddm.default")
(onFullSupported "nixos.tests.shadow")

View file

@ -531,7 +531,8 @@ in {
power-profiles-daemon = handleTest ./power-profiles-daemon.nix {};
pppd = handleTest ./pppd.nix {};
predictable-interface-names = handleTest ./predictable-interface-names.nix {};
printing = handleTest ./printing.nix {};
printing-socket = handleTest ./printing.nix { socket = true; };
printing-service = handleTest ./printing.nix { socket = false; };
privacyidea = handleTest ./privacyidea.nix {};
privoxy = handleTest ./privoxy.nix {};
prometheus = handleTest ./prometheus.nix {};

View file

@ -1,19 +1,31 @@
# Test printing via CUPS.
import ./make-test-python.nix ({pkgs, ... }:
let
printingServer = startWhenNeeded: {
services.printing.enable = true;
services.printing.stateless = true;
services.printing.startWhenNeeded = startWhenNeeded;
services.printing.listenAddresses = [ "*:631" ];
services.printing.defaultShared = true;
services.printing.extraConf = ''
<Location />
Order allow,deny
Allow from all
</Location>
'';
import ./make-test-python.nix (
{ pkgs
, socket ? true # whether to use socket activation
, ...
}:
{
name = "printing";
meta = with pkgs.lib.maintainers; {
maintainers = [ domenkozar eelco matthewbauer ];
};
nodes.server = { ... }: {
services.printing = {
enable = true;
stateless = true;
startWhenNeeded = socket;
listenAddresses = [ "*:631" ];
defaultShared = true;
extraConf = ''
<Location />
Order allow,deny
Allow from all
</Location>
'';
};
networking.firewall.allowedTCPPorts = [ 631 ];
# Add a HP Deskjet printer connected via USB to the server.
hardware.printers.ensurePrinters = [{
@ -22,32 +34,19 @@ let
model = "drv:///sample.drv/deskjet.ppd";
}];
};
printingClient = startWhenNeeded: {
nodes.client = { ... }: {
services.printing.enable = true;
services.printing.startWhenNeeded = startWhenNeeded;
services.printing.startWhenNeeded = socket;
# Add printer to the client as well, via IPP.
hardware.printers.ensurePrinters = [{
name = "DeskjetRemote";
deviceUri = "ipp://${if startWhenNeeded then "socketActivatedServer" else "serviceServer"}/printers/DeskjetLocal";
deviceUri = "ipp://server/printers/DeskjetLocal";
model = "drv:///sample.drv/deskjet.ppd";
}];
hardware.printers.ensureDefaultPrinter = "DeskjetRemote";
};
in {
name = "printing";
meta = with pkgs.lib.maintainers; {
maintainers = [ domenkozar eelco matthewbauer ];
};
nodes = {
socketActivatedServer = { ... }: (printingServer true);
serviceServer = { ... }: (printingServer false);
socketActivatedClient = { ... }: (printingClient true);
serviceClient = { ... }: (printingClient false);
};
testScript = ''
import os
import re
@ -55,75 +54,69 @@ in {
start_all()
with subtest("Make sure that cups is up on both sides and printers are set up"):
serviceServer.wait_for_unit("cups.service")
serviceClient.wait_for_unit("cups.service")
socketActivatedClient.wait_for_unit("ensure-printers.service")
server.wait_for_unit("cups.${if socket then "socket" else "service"}")
client.wait_for_unit("cups.${if socket then "socket" else "service"}")
assert "scheduler is running" in client.succeed("lpstat -r")
def test_printing(client, server):
assert "scheduler is running" in client.succeed("lpstat -r")
with subtest("UNIX socket is used for connections"):
assert "/var/run/cups/cups.sock" in client.succeed("lpstat -H")
with subtest("UNIX socket is used for connections"):
assert "/var/run/cups/cups.sock" in client.succeed("lpstat -H")
with subtest("HTTP server is available too"):
client.succeed("curl --fail http://localhost:631/")
client.succeed(f"curl --fail http://{server.name}:631/")
server.fail(f"curl --fail --connect-timeout 2 http://{client.name}:631/")
with subtest("HTTP server is available too"):
client.succeed("curl --fail http://localhost:631/")
client.succeed(f"curl --fail http://{server.name}:631/")
server.fail(f"curl --fail --connect-timeout 2 http://{client.name}:631/")
with subtest("LP status checks"):
assert "DeskjetRemote accepting requests" in client.succeed("lpstat -a")
assert "DeskjetLocal accepting requests" in client.succeed(
f"lpstat -h {server.name}:631 -a"
)
client.succeed("cupsdisable DeskjetRemote")
out = client.succeed("lpq")
print(out)
assert re.search(
"DeskjetRemote is not ready.*no entries",
client.succeed("lpq"),
flags=re.DOTALL,
)
client.succeed("cupsenable DeskjetRemote")
assert re.match(
"DeskjetRemote is ready.*no entries", client.succeed("lpq"), flags=re.DOTALL
with subtest("LP status checks"):
assert "DeskjetRemote accepting requests" in client.succeed("lpstat -a")
assert "DeskjetLocal accepting requests" in client.succeed(
f"lpstat -h {server.name}:631 -a"
)
client.succeed("cupsdisable DeskjetRemote")
out = client.succeed("lpq")
print(out)
assert re.search(
"DeskjetRemote is not ready.*no entries",
client.succeed("lpq"),
flags=re.DOTALL,
)
client.succeed("cupsenable DeskjetRemote")
assert re.match(
"DeskjetRemote is ready.*no entries", client.succeed("lpq"), flags=re.DOTALL
)
# Test printing various file types.
for file in [
"${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf",
"${pkgs.groff.doc}/share/doc/*/meref.ps",
"${pkgs.cups.out}/share/doc/cups/images/cups.png",
"${pkgs.pcre.doc}/share/doc/pcre/pcre.txt",
]:
file_name = os.path.basename(file)
with subtest(f"print {file_name}"):
# Print the file on the client.
print(client.succeed("lpq"))
client.succeed(f"lp {file}")
client.wait_until_succeeds(
f"lpq; lpq | grep -q -E 'active.*root.*{file_name}'"
)
# Test printing various file types.
for file in [
"${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf",
"${pkgs.groff.doc}/share/doc/*/meref.ps",
"${pkgs.cups.out}/share/doc/cups/images/cups.png",
"${pkgs.pcre.doc}/share/doc/pcre/pcre.txt",
]:
file_name = os.path.basename(file)
with subtest(f"print {file_name}"):
# Print the file on the client.
print(client.succeed("lpq"))
client.succeed(f"lp {file}")
client.wait_until_succeeds(
f"lpq; lpq | grep -q -E 'active.*root.*{file_name}'"
)
# Ensure that a raw PCL file appeared in the server's queue
# (showing that the right filters have been applied). Of
# course, since there is no actual USB printer attached, the
# file will stay in the queue forever.
server.wait_for_file("/var/spool/cups/d*-001")
server.wait_until_succeeds(f"lpq -a | grep -q -E '{file_name}'")
# Ensure that a raw PCL file appeared in the server's queue
# (showing that the right filters have been applied). Of
# course, since there is no actual USB printer attached, the
# file will stay in the queue forever.
server.wait_for_file("/var/spool/cups/d*-001")
server.wait_until_succeeds(f"lpq -a | grep -q -E '{file_name}'")
# Delete the job on the client. It should disappear on the
# server as well.
client.succeed("lprm")
client.wait_until_succeeds("lpq -a | grep -q -E 'no entries'")
# Delete the job on the client. It should disappear on the
# server as well.
client.succeed("lprm")
client.wait_until_succeeds("lpq -a | grep -q -E 'no entries'")
retry(lambda _: "no entries" in server.succeed("lpq -a"))
retry(lambda _: "no entries" in server.succeed("lpq -a"))
# The queue is empty already, so this should be safe.
# Otherwise, pairs of "c*"-"d*-001" files might persist.
server.execute("rm /var/spool/cups/*")
test_printing(serviceClient, serviceServer)
test_printing(socketActivatedClient, socketActivatedServer)
# The queue is empty already, so this should be safe.
# Otherwise, pairs of "c*"-"d*-001" files might persist.
server.execute("rm /var/spool/cups/*")
'';
})

View file

@ -38,6 +38,11 @@ stdenv.mkDerivation rec {
postPatch = ''
substituteInPlace cups/testfile.c \
--replace 'cupsFileFind("cat", "/bin' 'cupsFileFind("cat", "${coreutils}/bin'
# The cups.socket unit shouldn't be part of cups.service: stopping the
# service would stop the socket and break subsequent socket activations.
# See https://github.com/apple/cups/issues/6005
sed -i '/PartOf=cups.service/d' scheduler/cups.socket.in
'';
nativeBuildInputs = [ pkg-config removeReferencesTo ];