1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.pixiecore;
7in
8{
9 meta.maintainers = with maintainers; [ bbigras danderson ];
10
11 options = {
12 services.pixiecore = {
13 enable = mkEnableOption (lib.mdDoc "Pixiecore");
14
15 openFirewall = mkOption {
16 type = types.bool;
17 default = false;
18 description = lib.mdDoc ''
19 Open ports (67, 69 UDP and 4011, 'port', 'statusPort' TCP) in the firewall for Pixiecore.
20 '';
21 };
22
23 mode = mkOption {
24 description = lib.mdDoc "Which mode to use";
25 default = "boot";
26 type = types.enum [ "api" "boot" ];
27 };
28
29 debug = mkOption {
30 type = types.bool;
31 default = false;
32 description = lib.mdDoc "Log more things that aren't directly related to booting a recognized client";
33 };
34
35 dhcpNoBind = mkOption {
36 type = types.bool;
37 default = false;
38 description = lib.mdDoc "Handle DHCP traffic without binding to the DHCP server port";
39 };
40
41 kernel = mkOption {
42 type = types.str or types.path;
43 default = "";
44 description = lib.mdDoc "Kernel path. Ignored unless mode is set to 'boot'";
45 };
46
47 initrd = mkOption {
48 type = types.str or types.path;
49 default = "";
50 description = lib.mdDoc "Initrd path. Ignored unless mode is set to 'boot'";
51 };
52
53 cmdLine = mkOption {
54 type = types.str;
55 default = "";
56 description = lib.mdDoc "Kernel commandline arguments. Ignored unless mode is set to 'boot'";
57 };
58
59 listen = mkOption {
60 type = types.str;
61 default = "0.0.0.0";
62 description = lib.mdDoc "IPv4 address to listen on";
63 };
64
65 port = mkOption {
66 type = types.port;
67 default = 80;
68 description = lib.mdDoc "Port to listen on for HTTP";
69 };
70
71 statusPort = mkOption {
72 type = types.port;
73 default = 80;
74 description = lib.mdDoc "HTTP port for status information (can be the same as --port)";
75 };
76
77 apiServer = mkOption {
78 type = types.str;
79 example = "localhost:8080";
80 description = lib.mdDoc "host:port to connect to the API. Ignored unless mode is set to 'api'";
81 };
82
83 extraArguments = mkOption {
84 type = types.listOf types.str;
85 default = [];
86 description = lib.mdDoc "Additional command line arguments to pass to Pixiecore";
87 };
88 };
89 };
90
91 config = mkIf cfg.enable {
92 users.groups.pixiecore = {};
93 users.users.pixiecore = {
94 description = "Pixiecore daemon user";
95 group = "pixiecore";
96 isSystemUser = true;
97 };
98
99 networking.firewall = mkIf cfg.openFirewall {
100 allowedTCPPorts = [ 4011 cfg.port cfg.statusPort ];
101 allowedUDPPorts = [ 67 69 ];
102 };
103
104 systemd.services.pixiecore = {
105 description = "Pixiecore server";
106 after = [ "network.target"];
107 wants = [ "network.target"];
108 wantedBy = [ "multi-user.target"];
109 serviceConfig = {
110 User = "pixiecore";
111 Restart = "always";
112 AmbientCapabilities = [ "cap_net_bind_service" ] ++ optional cfg.dhcpNoBind "cap_net_raw";
113 ExecStart =
114 let
115 argString =
116 if cfg.mode == "boot"
117 then [ "boot" cfg.kernel ]
118 ++ optional (cfg.initrd != "") cfg.initrd
119 ++ optionals (cfg.cmdLine != "") [ "--cmdline" cfg.cmdLine ]
120 else [ "api" cfg.apiServer ];
121 in
122 ''
123 ${pkgs.pixiecore}/bin/pixiecore \
124 ${lib.escapeShellArgs argString} \
125 ${optionalString cfg.debug "--debug"} \
126 ${optionalString cfg.dhcpNoBind "--dhcp-no-bind"} \
127 --listen-addr ${lib.escapeShellArg cfg.listen} \
128 --port ${toString cfg.port} \
129 --status-port ${toString cfg.statusPort} \
130 ${escapeShellArgs cfg.extraArguments}
131 '';
132 };
133 };
134 };
135}