1# Global configuration for atop.
2
3{
4 config,
5 lib,
6 pkgs,
7 ...
8}:
9
10let
11 cfg = config.programs.atop;
12
13in
14{
15 ###### interface
16
17 options = {
18
19 programs.atop = rec {
20
21 enable = lib.mkEnableOption "Atop, a tool for monitoring system resources";
22
23 package = lib.mkPackageOption pkgs "atop" { };
24
25 netatop = {
26 enable = lib.mkOption {
27 type = lib.types.bool;
28 default = false;
29 description = ''
30 Whether to install and enable the netatop kernel module.
31 Note: this sets the kernel taint flag "O" for loading out-of-tree modules.
32 '';
33 };
34 package = lib.mkOption {
35 type = lib.types.package;
36 default = config.boot.kernelPackages.netatop;
37 defaultText = lib.literalExpression "config.boot.kernelPackages.netatop";
38 description = ''
39 Which package to use for netatop.
40 '';
41 };
42 };
43
44 atopgpu.enable = lib.mkOption {
45 type = lib.types.bool;
46 default = false;
47 description = ''
48 Whether to install and enable the atopgpud daemon to get information about
49 NVIDIA gpus.
50 '';
51 };
52
53 setuidWrapper.enable = lib.mkOption {
54 type = lib.types.bool;
55 default = false;
56 description = ''
57 Whether to install a setuid wrapper for Atop. This is required to use some of
58 the features as non-root user (e.g.: ipc information, netatop, atopgpu).
59 Atop tries to drop the root privileges shortly after starting.
60 '';
61 };
62
63 atopService.enable = lib.mkOption {
64 type = lib.types.bool;
65 default = true;
66 description = ''
67 Whether to enable the atop service responsible for storing statistics for
68 long-term analysis.
69 '';
70 };
71 atopRotateTimer.enable = lib.mkOption {
72 type = lib.types.bool;
73 default = true;
74 description = ''
75 Whether to enable the atop-rotate timer, which restarts the atop service
76 daily to make sure the data files are rotate.
77 '';
78 };
79 atopacctService.enable = lib.mkOption {
80 type = lib.types.bool;
81 default = true;
82 description = ''
83 Whether to enable the atopacct service which manages process accounting.
84 This allows Atop to gather data about processes that disappeared in between
85 two refresh intervals.
86 '';
87 };
88 settings = lib.mkOption {
89 type = lib.types.attrs;
90 default = { };
91 example = {
92 flags = "a1f";
93 interval = 5;
94 };
95 description = ''
96 Parameters to be written to {file}`/etc/atoprc`.
97 '';
98 };
99 };
100 };
101
102 config = lib.mkIf cfg.enable (
103 let
104 atop = if cfg.atopgpu.enable then (cfg.package.override { withAtopgpu = true; }) else cfg.package;
105 in
106 {
107 environment.etc = lib.mkIf (cfg.settings != { }) {
108 atoprc.text = lib.concatStrings (
109 lib.mapAttrsToList (n: v: ''
110 ${n} ${builtins.toString v}
111 '') cfg.settings
112 );
113 };
114 environment.systemPackages = [
115 atop
116 (lib.mkIf cfg.netatop.enable cfg.netatop.package)
117 ];
118 boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
119 systemd =
120 let
121 mkSystemd = type: name: restartTriggers: {
122 ${name} = {
123 inherit restartTriggers;
124 wantedBy = [
125 (
126 if type == "services" then
127 "multi-user.target"
128 else if type == "timers" then
129 "timers.target"
130 else
131 null
132 )
133 ];
134 };
135 };
136 mkService = mkSystemd "services";
137 mkTimer = mkSystemd "timers";
138 in
139 {
140 packages = [
141 atop
142 (lib.mkIf cfg.netatop.enable cfg.netatop.package)
143 ];
144 services = lib.mkMerge [
145 (lib.mkIf cfg.atopService.enable (
146 lib.recursiveUpdate (mkService "atop" [ atop ]) {
147 # always convert logs to newer version first
148 # XXX might trigger TimeoutStart but restarting atop.service will
149 # convert remainings logs and start eventually
150 atop.preStart = ''
151 set -e -u
152 shopt -s nullglob
153 rm -f "$LOGPATH"/atop_*.new
154 for logfile in "$LOGPATH"/atop_*
155 do
156 ${atop}/bin/atopconvert "$logfile" "$logfile".new
157 # only replace old file if version was upgraded to avoid
158 # false positives for atop-rotate.service
159 if ! ${pkgs.diffutils}/bin/cmp -s "$logfile" "$logfile".new
160 then
161 mv -v -f "$logfile".new "$logfile"
162 else
163 rm -f "$logfile".new
164 fi
165 done
166 '';
167 }
168 ))
169 (lib.mkIf cfg.atopacctService.enable (mkService "atopacct" [ atop ]))
170 (lib.mkIf cfg.netatop.enable (mkService "netatop" [ cfg.netatop.package ]))
171 (lib.mkIf cfg.atopgpu.enable (mkService "atopgpu" [ atop ]))
172 ];
173 timers = lib.mkIf cfg.atopRotateTimer.enable (mkTimer "atop-rotate" [ atop ]);
174 };
175
176 security.wrappers = lib.mkIf cfg.setuidWrapper.enable {
177 atop = {
178 setuid = true;
179 owner = "root";
180 group = "root";
181 source = "${atop}/bin/atop";
182 };
183 };
184 }
185 );
186}