From b77f1bc48a51a5019dc16c146665f98b38ff8857 Mon Sep 17 00:00:00 2001 From: sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> Date: Sat, 20 Mar 2021 23:45:47 +0100 Subject: [PATCH 1/7] inspircd: init at 3.9.0 Packaging inspircd is relatively straightforward, once we adapt to the slightly strange Perl configure script and it's firm opinion that $prefix/usr should exist. Most complexity in this derivation stems from the following: * inspircd has modules which users can load dynamically in the form of shared objects that link against other libraries for various tasks * inspircd is licensed exclusively under the GPL version 2. * Some of the libraries inspircd modules link against are GPL 2 incompatible (GPL 3, ASL 2.0) and we therefore must not distribute these in binary form. * Some modules combine GPL 2 code of inspircd and libc into a shared object and may not be redistributed in binary form depending on the license of the libc. Similarly for libc++. Open Question: Does the fact that we may build the inspircd binary, i. e. link against libc and libc++ imply that we can do this? https://docs.inspircd.org/packaging/ seems to imply this is not the case. Thus we have some additional code which a) determines the set of modules we should enable by default (the largest possible set as upstream recommends it) and b) collects all applying licenses into meta.license. --- pkgs/servers/irc/inspircd/default.nix | 218 ++++++++++++++++++++++++++ pkgs/top-level/all-packages.nix | 4 + 2 files changed, 222 insertions(+) create mode 100644 pkgs/servers/irc/inspircd/default.nix diff --git a/pkgs/servers/irc/inspircd/default.nix b/pkgs/servers/irc/inspircd/default.nix new file mode 100644 index 00000000000..f1df10514e6 --- /dev/null +++ b/pkgs/servers/irc/inspircd/default.nix @@ -0,0 +1,218 @@ +let + # inspircd ships a few extra modules that users can load + # via configuration. Upstream thus recommends to ship as + # many of them as possible. There is however a problem: + # inspircd is licensed under the GPL version 2 only and + # some modules link libraries that are incompatible with + # the GPL 2. Therefore we can't provide them as binaries + # via our binary-caches, but users should still be able + # to override this package and build the incompatible + # modules themselves. + # + # This means for us we need to a) prevent hydra from + # building a module set with a GPL incompatibility + # and b) dynamically figure out the largest possible + # set of modules to use depending on stdenv, because + # the used libc needs to be compatible as well. + # + # For an overview of all modules and their licensing + # situation, see https://docs.inspircd.org/packaging/ + + # Predicate for checking license compatibility with + # GPLv2. Since this is _only_ used for libc compatibility + # checking, only whitelist licenses used by notable + # libcs in nixpkgs (musl and glibc). + compatible = lib: drv: + lib.any (lic: lic == drv.meta.license) [ + lib.licenses.mit # musl + lib.licenses.lgpl2Plus # glibc + ]; + + # compatible if libc is compatible + libcModules = [ + "regex_posix" + "sslrehashsignal" + ]; + + # compatible if libc++ is compatible + # TODO(sternenseemann): + # we could enable "regex_stdlib" automatically, but only if + # we are using libcxxStdenv which is compatible with GPLv2, + # since the gcc libstdc++ license is GPLv2-incompatible + libcxxModules = [ + "regex_stdlib" + ]; + + compatibleModules = lib: stdenv: [ + # GPLv2 compatible dependencies + "argon2" + "ldap" + "mysql" + "pgsql" + "regex_pcre" + "regex_re2" + "regex_tre" + "sqlite3" + "ssl_gnutls" + ] ++ lib.optionals (compatible lib stdenv.cc.libc) libcModules; + +in + +{ lib +, stdenv +, fetchFromGitHub +, fetchpatch +, perl +, pkg-config +, libargon2 +, openldap +, postgresql +, libmysqlclient +, pcre +, tre +, re2 +, sqlite +, gnutls +, libmaxminddb +, openssl +, mbedtls +# For a full list of module names, see https://docs.inspircd.org/packaging/ +, extraModules ? compatibleModules lib stdenv +}: + +let + extras = { + # GPLv2 compatible + argon2 = [ + (libargon2 // { + meta = libargon2.meta // { + # use libargon2 as CC0 since ASL20 is GPLv2-incompatible + # updating this here is important that meta.license is accurate + license = lib.licenses.cc0; + }; + }) + ]; + ldap = [ openldap ]; + mysql = [ libmysqlclient ]; + pgsql = [ postgresql ]; + regex_pcre = [ pcre ]; + regex_re2 = [ re2 ]; + regex_tre = [ tre ]; + sqlite3 = [ sqlite ]; + ssl_gnutls = [ gnutls ]; + # depends on stdenv.cc.libc + regex_posix = []; + sslrehashsignal = []; + # depends on used libc++ + regex_stdlib = []; + # GPLv2 incompatible + geo_maxmind = [ libmaxminddb ]; + ssl_mbedtls = [ mbedtls ]; + ssl_openssl = [ openssl ]; + }; + + # buildInputs necessary for the enabled extraModules + extraInputs = lib.concatMap + (m: extras."${m}" or (builtins.throw "Unknown extra module ${m}")) + extraModules; + + # if true, we can't provide a binary version of this + # package without violating the GPL 2 + gpl2Conflict = + let + allowed = compatibleModules lib stdenv; + in + !lib.all (lib.flip lib.elem allowed) extraModules; + + # return list of the license(s) of the given derivation + getLicenses = drv: + let + lics = drv.meta.license or []; + in + if lib.isAttrs lics || lib.isString lics + then [ lics ] + else lics; + + # Whether any member of list1 is also member of list2, i. e. set intersection. + anyMembers = list1: list2: + lib.any (m1: lib.elem m1 list2) list1; + +in + +stdenv.mkDerivation rec { + pname = "inspircd"; + version = "3.9.0"; + + src = fetchFromGitHub { + owner = pname; + repo = pname; + rev = "v${version}"; + sha256 = "0x3paasf4ynx4ddky2nq613vyirbhfnxzkjq148k7154pz3q426s"; + }; + + patches = [ + # Remove at next release, makes --prefix and --system work together + (fetchpatch { + url = "https://github.com/inspircd/inspircd/commit/b378b5087b41f72a1624ebb58990180e0b0140aa.patch"; + sha256 = "0c0fmhjbkfh2r1cmjrm5d4whcignwsyi6kwkhmcvqy9mv99pj2ir"; + }) + ]; + + nativeBuildInputs = [ + perl + pkg-config + ]; + buildInputs = extraInputs; + + preConfigure = '' + patchShebangs configure make/*.pl + # configure is executed twice, once to set the extras + # to use and once to do the Makefile setup + ./configure --enable-extras \ + ${lib.escapeShellArg (lib.concatStringsSep " " extraModules)} + ''; + + configureFlags = [ + "--disable-auto-extras" + "--distribution-label" "nixpkgs${version}" + "--system" + "--uid" "0" + "--gid" "0" + "--prefix" (placeholder "out") + ]; + + postInstall = '' + # installs to $out/usr by default unfortunately + mv "$out/usr/lib" "$out/lib" + mv "$out/usr/sbin" "$out/bin" + mv "$out/usr/share" "$out/share" + rm -r "$out/usr" + rm -r "$out/var" # only empty directories + rm -r "$out/etc" # only contains documentation + ''; + + enableParallelBuilding = true; + + meta = { + description = "A modular C++ IRC server"; + license = [ lib.licenses.gpl2Only ] + ++ lib.concatMap getLicenses extraInputs + ++ lib.optionals (anyMembers extraModules libcModules) (getLicenses stdenv.cc.libc) + # FIXME(sternenseemann): get license of used lib(std)c++ somehow + ++ lib.optional (anyMembers extraModules libcxxModules) "Unknown" + # Hack: Definitely prevent a hydra from building this package on + # a GPL 2 incompatibility even if it is not in a top-level attribute, + # but pulled in indirectly somehow. + ++ lib.optional gpl2Conflict lib.licenses.unfree; + maintainers = [ lib.maintainers.sternenseemann ]; + # windows is theoretically possible, but requires extra work + # which I am not willing to do and can't test. + # https://github.com/inspircd/inspircd/blob/master/win/README.txt + platforms = lib.platforms.unix; + homepage = "https://www.inspircd.org/"; + } // lib.optionalAttrs gpl2Conflict { + # make sure we never distribute a GPLv2-violating module + # in binary form. They can be built locally of course. + hydraPlatforms = []; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index ccde125fc37..edae119c244 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -18229,6 +18229,10 @@ in theme-spring = callPackage ../servers/icingaweb2/theme-spring { }; }; + inspircd = callPackage ../servers/irc/inspircd { }; + + inspircdMinimal = inspircd.override { extraModules = []; }; + imgproxy = callPackage ../servers/imgproxy { }; ircdog = callPackage ../applications/networking/irc/ircdog { }; From 0fd939c6ea34b2b600e42469e764f53356694887 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Sun, 21 Mar 2021 14:08:01 +0100 Subject: [PATCH 2/7] fixup! inspircd: init at 3.9.0 --- pkgs/servers/irc/inspircd/default.nix | 49 +++++++++++++-------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/pkgs/servers/irc/inspircd/default.nix b/pkgs/servers/irc/inspircd/default.nix index f1df10514e6..6339ca67b93 100644 --- a/pkgs/servers/irc/inspircd/default.nix +++ b/pkgs/servers/irc/inspircd/default.nix @@ -88,6 +88,7 @@ let meta = libargon2.meta // { # use libargon2 as CC0 since ASL20 is GPLv2-incompatible # updating this here is important that meta.license is accurate + # libargon2 is licensed under either ASL20 or CC0. license = lib.licenses.cc0; }; }) @@ -150,13 +151,7 @@ stdenv.mkDerivation rec { sha256 = "0x3paasf4ynx4ddky2nq613vyirbhfnxzkjq148k7154pz3q426s"; }; - patches = [ - # Remove at next release, makes --prefix and --system work together - (fetchpatch { - url = "https://github.com/inspircd/inspircd/commit/b378b5087b41f72a1624ebb58990180e0b0140aa.patch"; - sha256 = "0c0fmhjbkfh2r1cmjrm5d4whcignwsyi6kwkhmcvqy9mv99pj2ir"; - }) - ]; + outputs = [ "bin" "lib" "man" "doc" "out" ]; nativeBuildInputs = [ perl @@ -164,31 +159,35 @@ stdenv.mkDerivation rec { ]; buildInputs = extraInputs; - preConfigure = '' + configurePhase = '' patchShebangs configure make/*.pl + # configure is executed twice, once to set the extras # to use and once to do the Makefile setup - ./configure --enable-extras \ + ./configure \ + --enable-extras \ ${lib.escapeShellArg (lib.concatStringsSep " " extraModules)} + + # this manually sets the flags instead of using configureFlags, because otherwise stdenv passes flags like --bindir, which make configure fail + ./configure \ + --disable-auto-extras \ + --distribution-label nixpkgs${version} \ + --uid 0 \ + --gid 0 \ + --binary-dir ${placeholder "bin"}/bin \ + --config-dir /etc/inspircd \ + --data-dir ${placeholder "lib"}/lib/inspircd \ + --example-dir ${placeholder "doc"}/share/doc/inspircd \ + --log-dir /var/log/inspircd \ + --manual-dir ${placeholder "man"}/share/man/man1 \ + --module-dir ${placeholder "lib"}/lib/inspircd \ + --runtime-dir /var/run \ + --script-dir ${placeholder "bin"}/share/inspircd \ ''; - configureFlags = [ - "--disable-auto-extras" - "--distribution-label" "nixpkgs${version}" - "--system" - "--uid" "0" - "--gid" "0" - "--prefix" (placeholder "out") - ]; - postInstall = '' - # installs to $out/usr by default unfortunately - mv "$out/usr/lib" "$out/lib" - mv "$out/usr/sbin" "$out/bin" - mv "$out/usr/share" "$out/share" - rm -r "$out/usr" - rm -r "$out/var" # only empty directories - rm -r "$out/etc" # only contains documentation + # for some reasons the executables are not executable + chmod +x $bin/bin/* ''; enableParallelBuilding = true; From 4048b39fc1a5c17b4c5052a74a84f3c75c5425e6 Mon Sep 17 00:00:00 2001 From: sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> Date: Mon, 22 Mar 2021 14:32:46 +0100 Subject: [PATCH 3/7] nixos/modules/inspircd: add simplistic module and nixos test --- nixos/modules/module-list.nix | 1 + .../modules/services/networking/inspircd.nix | 58 +++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/inspircd.nix | 96 +++++++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 nixos/modules/services/networking/inspircd.nix create mode 100644 nixos/tests/inspircd.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 07774dd1d29..4a63a09ab84 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -682,6 +682,7 @@ ./services/networking/i2p.nix ./services/networking/icecream/scheduler.nix ./services/networking/icecream/daemon.nix + ./services/networking/inspircd.nix ./services/networking/iodine.nix ./services/networking/iperf3.nix ./services/networking/ircd-hybrid/default.nix diff --git a/nixos/modules/services/networking/inspircd.nix b/nixos/modules/services/networking/inspircd.nix new file mode 100644 index 00000000000..fcac8d34cce --- /dev/null +++ b/nixos/modules/services/networking/inspircd.nix @@ -0,0 +1,58 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.inspircd; + + configFile = pkgs.writeText "inspircd.conf" cfg.config; + +in { + options = { + services.inspircd = { + enable = lib.mkEnableOption "InspIRCd"; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.inspircd; + defaultText = lib.literalExample "pkgs.inspircd"; + example = lib.literalExample "pkgs.inspircdMinimal"; + description = '' + The InspIRCd package to use. This is mainly useful + to specify an overridden version of the + pkgs.inspircd dervivation, for + example if you want to use a more minimal InspIRCd + distribution with less modules enabled or with + modules enabled which can't be distributed in binary + form due to licensing issues. + ''; + }; + + config = lib.mkOption { + type = lib.types.lines; + description = '' + Verbatim inspircd.conf file. + For a list of options, consult the + InspIRCd documentation, the + Module documentation + and the example configuration files distributed + with pkgs.inspircd.doc + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.inspircd = { + description = "InspIRCd - the stable, high-performance and modular Internet Relay Chat Daemon"; + wantedBy = [ "multi-user.target" ]; + requires = [ "network.target" ]; + + serviceConfig = { + Type = "simple"; + ExecStart = '' + ${lib.getBin cfg.package}/bin/inspircd start --config ${configFile} --nofork --nopid + ''; + DynamicUser = true; + }; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3ce71b0abe6..7d19e7309bc 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -176,6 +176,7 @@ in initrd-network-ssh = handleTest ./initrd-network-ssh {}; initrdNetwork = handleTest ./initrd-network.nix {}; initrd-secrets = handleTest ./initrd-secrets.nix {}; + inspircd = handleTest ./inspircd.nix {}; installer = handleTest ./installer.nix {}; iodine = handleTest ./iodine.nix {}; ipfs = handleTest ./ipfs.nix {}; diff --git a/nixos/tests/inspircd.nix b/nixos/tests/inspircd.nix new file mode 100644 index 00000000000..b2975091d36 --- /dev/null +++ b/nixos/tests/inspircd.nix @@ -0,0 +1,96 @@ +let + clients = [ + "ircclient1" + "ircclient2" + ]; + server = "inspircd"; + ircPort = 6667; + channel = "nixos-cat"; + iiDir = "/tmp/irc"; +in + +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "inspircd"; + nodes = { + "${server}" = { + networking.firewall.allowedTCPPorts = [ ircPort ]; + services.inspircd = { + enable = true; + package = pkgs.inspircdMinimal; + config = '' + + + ''; + }; + }; + } // lib.listToAttrs (builtins.map (client: lib.nameValuePair client { + imports = [ + ./common/user-account.nix + ]; + + systemd.services.ii = { + requires = [ "network.target" ]; + wantedBy = [ "default.target" ]; + + serviceConfig = { + Type = "simple"; + ExecPreStartPre = "mkdir -p ${iiDir}"; + ExecStart = '' + ${lib.getBin pkgs.ii}/bin/ii -n ${client} -s ${server} -i ${iiDir} + ''; + User = "alice"; + }; + }; + }) clients); + + testScript = + let + msg = client: "Hello, my name is ${client}"; + clientScript = client: [ + '' + ${client}.wait_for_unit("network.target") + ${client}.systemctl("start ii") + ${client}.wait_for_unit("ii") + ${client}.wait_for_file("${iiDir}/${server}/out") + '' + # wait until first PING from server arrives before joining, + # so we don't try it too early + '' + ${client}.wait_until_succeeds("grep 'PING' ${iiDir}/${server}/out") + '' + # join ${channel} + '' + ${client}.succeed("echo '/j #${channel}' > ${iiDir}/${server}/in") + ${client}.wait_for_file("${iiDir}/${server}/#${channel}/in") + '' + # send a greeting + '' + ${client}.succeed( + "echo '${msg client}' > ${iiDir}/${server}/#${channel}/in" + ) + '' + # check that all greetings arrived on all clients + ] ++ builtins.map (other: '' + ${client}.succeed( + "grep '${msg other}$' ${iiDir}/${server}/#${channel}/out" + ) + '') clients ++ [ + "# trailing comment to please reformatter :)" + ]; + + # foldl', but requires a non-empty list instead of a start value + reduce = f: list: + builtins.foldl' f (builtins.head list) (builtins.tail list); + in '' + start_all() + ${server}.wait_for_open_port(${toString ircPort}) + + # run clientScript for all clients so that every list + # entry is executed by every client before advancing + # to the next one. + ${lib.concatStringsSep "\n" + (reduce + (a: b: lib.zipListsWith (cs: c: cs + "\n" + c) a b) + (builtins.map clientScript clients))} + ''; +}) From 66454f0e5a1720b8ac9886ef79ce863b8f6ee0a0 Mon Sep 17 00:00:00 2001 From: sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> Date: Mon, 22 Mar 2021 14:44:45 +0100 Subject: [PATCH 4/7] !fixup get rid of trailing comment hack --- nixos/tests/inspircd.nix | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/nixos/tests/inspircd.nix b/nixos/tests/inspircd.nix index b2975091d36..ceeca970f5f 100644 --- a/nixos/tests/inspircd.nix +++ b/nixos/tests/inspircd.nix @@ -74,9 +74,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { ${client}.succeed( "grep '${msg other}$' ${iiDir}/${server}/#${channel}/out" ) - '') clients ++ [ - "# trailing comment to please reformatter :)" - ]; + '') clients; # foldl', but requires a non-empty list instead of a start value reduce = f: list: @@ -88,9 +86,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { # run clientScript for all clients so that every list # entry is executed by every client before advancing # to the next one. - ${lib.concatStringsSep "\n" - (reduce - (a: b: lib.zipListsWith (cs: c: cs + "\n" + c) a b) - (builtins.map clientScript clients))} - ''; + '' + lib.concatStrings + (reduce + (a: b: lib.zipListsWith (cs: c: cs + "\n" + c) a b) + (builtins.map clientScript clients)); }) From 726db56d68cc5c017b9fe58600543825655f362e Mon Sep 17 00:00:00 2001 From: sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> Date: Mon, 22 Mar 2021 14:52:13 +0100 Subject: [PATCH 5/7] !fixup simplify zipListsWith call --- nixos/tests/inspircd.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/tests/inspircd.nix b/nixos/tests/inspircd.nix index ceeca970f5f..f4d82054011 100644 --- a/nixos/tests/inspircd.nix +++ b/nixos/tests/inspircd.nix @@ -88,6 +88,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { # to the next one. '' + lib.concatStrings (reduce - (a: b: lib.zipListsWith (cs: c: cs + "\n" + c) a b) + (lib.zipListsWith (cs: c: cs + c)) (builtins.map clientScript clients)); }) From 19225b869015d37a14d7c6604a7ef89808d270eb Mon Sep 17 00:00:00 2001 From: sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> Date: Mon, 22 Mar 2021 15:12:27 +0100 Subject: [PATCH 6/7] !fixup add nixos tests to passthru.tests --- pkgs/servers/irc/inspircd/default.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/servers/irc/inspircd/default.nix b/pkgs/servers/irc/inspircd/default.nix index 6339ca67b93..f907e337ce6 100644 --- a/pkgs/servers/irc/inspircd/default.nix +++ b/pkgs/servers/irc/inspircd/default.nix @@ -61,7 +61,7 @@ in { lib , stdenv , fetchFromGitHub -, fetchpatch +, nixosTests , perl , pkg-config , libargon2 @@ -192,6 +192,10 @@ stdenv.mkDerivation rec { enableParallelBuilding = true; + passthru.tests = { + nixos-test = nixosTests.inspircd; + }; + meta = { description = "A modular C++ IRC server"; license = [ lib.licenses.gpl2Only ] From 76d9fe7629036a065b80d795ec7a3405ea7bd232 Mon Sep 17 00:00:00 2001 From: sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> Date: Mon, 22 Mar 2021 15:19:49 +0100 Subject: [PATCH 7/7] !fixup add myself as maintainer for the module --- nixos/modules/services/networking/inspircd.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nixos/modules/services/networking/inspircd.nix b/nixos/modules/services/networking/inspircd.nix index fcac8d34cce..8cb2b406ee2 100644 --- a/nixos/modules/services/networking/inspircd.nix +++ b/nixos/modules/services/networking/inspircd.nix @@ -6,6 +6,10 @@ let configFile = pkgs.writeText "inspircd.conf" cfg.config; in { + meta = { + maintainers = [ lib.maintainers.sternenseemann ]; + }; + options = { services.inspircd = { enable = lib.mkEnableOption "InspIRCd";