blob: 2b2a68c2a478e464d2011b8ac6b444fcf7acd7e0 [file] [log] [blame]
Nathan Scott36389fb2021-03-23 15:04:54 +11001/*
2htop - CommandLine.c
3(C) 2004-2011 Hisham H. Muhammad
4(C) 2020-2021 htop dev team
Daniel Lange94ad1112021-09-22 11:33:00 +02005Released under the GNU GPLv2+, see the COPYING file
Nathan Scott36389fb2021-03-23 15:04:54 +11006in the source distribution for its full text.
7*/
8
9#include "config.h" // IWYU pragma: keep
10
11#include "CommandLine.h"
12
13#include <assert.h>
набdfa62502021-10-04 15:08:55 +020014#include <ctype.h>
Nathan Scott36389fb2021-03-23 15:04:54 +110015#include <getopt.h>
16#include <locale.h>
17#include <stdbool.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <time.h>
22#include <unistd.h>
23
24#include "Action.h"
25#include "CRT.h"
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +020026#include "DynamicColumn.h"
Nathan Scottf0ed0fd2021-06-23 17:44:56 +100027#include "DynamicMeter.h"
Nathan Scott36389fb2021-03-23 15:04:54 +110028#include "Hashtable.h"
29#include "Header.h"
30#include "IncSet.h"
31#include "MainPanel.h"
32#include "MetersPanel.h"
33#include "Panel.h"
34#include "Platform.h"
35#include "Process.h"
36#include "ProcessList.h"
37#include "ProvideCurses.h"
38#include "ScreenManager.h"
39#include "Settings.h"
40#include "UsersTable.h"
41#include "XUtils.h"
42
43
44static void printVersionFlag(const char* name) {
45 printf("%s " VERSION "\n", name);
46}
47
48static void printHelpFlag(const char* name) {
49 printf("%s " VERSION "\n"
Benny Baumann68edf922021-07-14 19:20:16 +020050 COPYRIGHT "\n"
Daniel Lange94ad1112021-09-22 11:33:00 +020051 "Released under the GNU GPLv2+.\n\n"
Benny Baumann68edf922021-07-14 19:20:16 +020052 "-C --no-color Use a monochrome color scheme\n"
53 "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
54 "-F --filter=FILTER Show only the commands matching the given filter\n"
55 "-h --help Print this help screen\n"
Daniel Langedf955c82022-04-07 12:33:22 +020056 "-H --highlight-changes[=DELAY] Highlight new and old processes\n", name);
Daniel Lange6a7b3fd2022-04-07 11:52:22 +020057#ifdef HAVE_GETMOUSE
Daniel Langedf955c82022-04-07 12:33:22 +020058 printf("-M --no-mouse Disable the mouse\n");
Daniel Lange6a7b3fd2022-04-07 11:52:22 +020059#endif
Daniel Langedf955c82022-04-07 12:33:22 +020060 printf("-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
Benny Baumann68edf922021-07-14 19:20:16 +020061 " --readonly Disable all system and process changing features\n"
62 "-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
63 "-t --tree Show the tree view (can be combined with -s)\n"
64 "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
65 "-U --no-unicode Do not use unicode but plain ASCII\n"
Daniel Langedf955c82022-04-07 12:33:22 +020066 "-V --version Print version info\n");
Nathan Scott36389fb2021-03-23 15:04:54 +110067 Platform_longOptionsUsage(name);
68 printf("\n"
Benny Baumann68edf922021-07-14 19:20:16 +020069 "Press F1 inside %s for online help.\n"
70 "See 'man %s' for more information.\n", name, name);
Nathan Scott36389fb2021-03-23 15:04:54 +110071}
72
73// ----------------------------------------
74
75typedef struct CommandLineSettings_ {
76 Hashtable* pidMatchList;
77 char* commFilter;
78 uid_t userId;
79 int sortKey;
80 int delay;
81 bool useColors;
Christian Göttsche1b5e0072022-08-09 20:12:08 +020082#ifdef HAVE_GETMOUSE
Nathan Scott36389fb2021-03-23 15:04:54 +110083 bool enableMouse;
Christian Göttsche1b5e0072022-08-09 20:12:08 +020084#endif
Nathan Scott36389fb2021-03-23 15:04:54 +110085 bool treeView;
86 bool allowUnicode;
87 bool highlightChanges;
88 int highlightDelaySecs;
Christian Göttsche36880cd2021-01-21 20:27:37 +010089 bool readonly;
Nathan Scott36389fb2021-03-23 15:04:54 +110090} CommandLineSettings;
91
Nathan Scott14da84f2023-04-04 16:47:11 +100092static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettings* flags) {
Nathan Scott36389fb2021-03-23 15:04:54 +110093
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +030094 *flags = (CommandLineSettings) {
Nathan Scott36389fb2021-03-23 15:04:54 +110095 .pidMatchList = NULL,
96 .commFilter = NULL,
97 .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
98 .sortKey = 0,
99 .delay = -1,
100 .useColors = true,
Christian Göttsche1b5e0072022-08-09 20:12:08 +0200101#ifdef HAVE_GETMOUSE
Nathan Scott36389fb2021-03-23 15:04:54 +1100102 .enableMouse = true,
Christian Göttsche1b5e0072022-08-09 20:12:08 +0200103#endif
Nathan Scott36389fb2021-03-23 15:04:54 +1100104 .treeView = false,
105 .allowUnicode = true,
106 .highlightChanges = false,
107 .highlightDelaySecs = -1,
Christian Göttsche36880cd2021-01-21 20:27:37 +0100108 .readonly = false,
Nathan Scott36389fb2021-03-23 15:04:54 +1100109 };
110
111 const struct option long_opts[] =
112 {
113 {"help", no_argument, 0, 'h'},
114 {"version", no_argument, 0, 'V'},
115 {"delay", required_argument, 0, 'd'},
116 {"sort-key", required_argument, 0, 's'},
117 {"user", optional_argument, 0, 'u'},
118 {"no-color", no_argument, 0, 'C'},
119 {"no-colour", no_argument, 0, 'C'},
120 {"no-mouse", no_argument, 0, 'M'},
121 {"no-unicode", no_argument, 0, 'U'},
122 {"tree", no_argument, 0, 't'},
123 {"pid", required_argument, 0, 'p'},
124 {"filter", required_argument, 0, 'F'},
125 {"highlight-changes", optional_argument, 0, 'H'},
Christian Göttsche36880cd2021-01-21 20:27:37 +0100126 {"readonly", no_argument, 0, 128},
Nathan Scott36389fb2021-03-23 15:04:54 +1100127 PLATFORM_LONG_OPTIONS
Benny Baumann0d85af22021-07-14 19:18:27 +0200128 {0, 0, 0, 0}
Nathan Scott36389fb2021-03-23 15:04:54 +1100129 };
130
Benny Baumann0d85af22021-07-14 19:18:27 +0200131 int opt, opti = 0;
Nathan Scott36389fb2021-03-23 15:04:54 +1100132 /* Parse arguments */
133 while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
Benny Baumanne7f8d7b2021-07-14 19:11:18 +0200134 if (opt == EOF)
135 break;
Nathan Scott36389fb2021-03-23 15:04:54 +1100136 switch (opt) {
137 case 'h':
138 printHelpFlag(program);
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300139 return STATUS_OK_EXIT;
Nathan Scott36389fb2021-03-23 15:04:54 +1100140 case 'V':
141 printVersionFlag(program);
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300142 return STATUS_OK_EXIT;
Nathan Scott36389fb2021-03-23 15:04:54 +1100143 case 's':
144 assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
145 if (String_eq(optarg, "help")) {
146 for (int j = 1; j < LAST_PROCESSFIELD; j++) {
147 const char* name = Process_fields[j].name;
148 const char* description = Process_fields[j].description;
Benny Baumann40104582022-10-22 19:19:39 +0200149 if (name)
150 printf("%19s %s\n", name, description);
Nathan Scott36389fb2021-03-23 15:04:54 +1100151 }
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300152 return STATUS_OK_EXIT;
Nathan Scott36389fb2021-03-23 15:04:54 +1100153 }
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300154 flags->sortKey = 0;
Nathan Scott36389fb2021-03-23 15:04:54 +1100155 for (int j = 1; j < LAST_PROCESSFIELD; j++) {
156 if (Process_fields[j].name == NULL)
157 continue;
158 if (String_eq(optarg, Process_fields[j].name)) {
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300159 flags->sortKey = j;
Nathan Scott36389fb2021-03-23 15:04:54 +1100160 break;
161 }
162 }
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300163 if (flags->sortKey == 0) {
Nathan Scott36389fb2021-03-23 15:04:54 +1100164 fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300165 return STATUS_ERROR_EXIT;
Nathan Scott36389fb2021-03-23 15:04:54 +1100166 }
167 break;
168 case 'd':
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300169 if (sscanf(optarg, "%16d", &(flags->delay)) == 1) {
Benny Baumann40104582022-10-22 19:19:39 +0200170 if (flags->delay < 1)
171 flags->delay = 1;
172 if (flags->delay > 100)
173 flags->delay = 100;
Nathan Scott36389fb2021-03-23 15:04:54 +1100174 } else {
175 fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300176 return STATUS_ERROR_EXIT;
Nathan Scott36389fb2021-03-23 15:04:54 +1100177 }
178 break;
179 case 'u':
180 {
Benny Baumann40104582022-10-22 19:19:39 +0200181 const char* username = optarg;
Nathan Scott36389fb2021-03-23 15:04:54 +1100182 if (!username && optind < argc && argv[optind] != NULL &&
183 (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
184 username = argv[optind++];
185 }
186
187 if (!username) {
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300188 flags->userId = geteuid();
189 } else if (!Action_setUserOnly(username, &(flags->userId))) {
Benny Baumann40104582022-10-22 19:19:39 +0200190 for (const char* itr = username; *itr; ++itr)
набdfa62502021-10-04 15:08:55 +0200191 if (!isdigit((unsigned char)*itr)) {
192 fprintf(stderr, "Error: invalid user \"%s\".\n", username);
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300193 return STATUS_ERROR_EXIT;
набdfa62502021-10-04 15:08:55 +0200194 }
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300195 flags->userId = atol(username);
Nathan Scott36389fb2021-03-23 15:04:54 +1100196 }
197 break;
198 }
199 case 'C':
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300200 flags->useColors = false;
Nathan Scott36389fb2021-03-23 15:04:54 +1100201 break;
202 case 'M':
niae8f27eb2021-07-14 21:07:43 +0200203#ifdef HAVE_GETMOUSE
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300204 flags->enableMouse = false;
niae8f27eb2021-07-14 21:07:43 +0200205#endif
Nathan Scott36389fb2021-03-23 15:04:54 +1100206 break;
207 case 'U':
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300208 flags->allowUnicode = false;
Nathan Scott36389fb2021-03-23 15:04:54 +1100209 break;
210 case 't':
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300211 flags->treeView = true;
Nathan Scott36389fb2021-03-23 15:04:54 +1100212 break;
213 case 'p': {
214 assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
215 char* argCopy = xStrdup(optarg);
216 char* saveptr;
217 const char* pid = strtok_r(argCopy, ",", &saveptr);
218
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300219 if (!flags->pidMatchList) {
220 flags->pidMatchList = Hashtable_new(8, false);
Nathan Scott36389fb2021-03-23 15:04:54 +1100221 }
222
Benny Baumann40104582022-10-22 19:19:39 +0200223 while (pid) {
224 unsigned int num_pid = atoi(pid);
225 // deepcode ignore CastIntegerToAddress: we just want a non-NULL pointer here
226 Hashtable_put(flags->pidMatchList, num_pid, (void*) 1);
227 pid = strtok_r(NULL, ",", &saveptr);
Nathan Scott36389fb2021-03-23 15:04:54 +1100228 }
229 free(argCopy);
230
231 break;
232 }
233 case 'F': {
234 assert(optarg);
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300235 free_and_xStrdup(&flags->commFilter, optarg);
Nathan Scott36389fb2021-03-23 15:04:54 +1100236 break;
237 }
238 case 'H': {
Benny Baumann40104582022-10-22 19:19:39 +0200239 const char* delay = optarg;
Nathan Scott36389fb2021-03-23 15:04:54 +1100240 if (!delay && optind < argc && argv[optind] != NULL &&
241 (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
Benny Baumann40104582022-10-22 19:19:39 +0200242 delay = argv[optind++];
Nathan Scott36389fb2021-03-23 15:04:54 +1100243 }
244 if (delay) {
Benny Baumann40104582022-10-22 19:19:39 +0200245 if (sscanf(delay, "%16d", &(flags->highlightDelaySecs)) == 1) {
246 if (flags->highlightDelaySecs < 1)
247 flags->highlightDelaySecs = 1;
248 } else {
249 fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
250 return STATUS_ERROR_EXIT;
251 }
Nathan Scott36389fb2021-03-23 15:04:54 +1100252 }
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300253 flags->highlightChanges = true;
Nathan Scott36389fb2021-03-23 15:04:54 +1100254 break;
255 }
Christian Göttsche36880cd2021-01-21 20:27:37 +0100256 case 128:
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300257 flags->readonly = true;
Christian Göttsche36880cd2021-01-21 20:27:37 +0100258 break;
Nathan Scott36389fb2021-03-23 15:04:54 +1100259
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300260 default: {
261 CommandLineStatus status;
262 if ((status = Platform_getLongOption(opt, argc, argv)) != STATUS_OK)
263 return status;
264 break;
265 }
Nathan Scott36389fb2021-03-23 15:04:54 +1100266 }
267 }
Christian Göttsche2f387af2022-10-20 14:57:42 +0200268
269 if (optind < argc) {
270 fprintf(stderr, "Error: unsupported non-option ARGV-elements:");
271 while (optind < argc)
272 fprintf(stderr, " %s", argv[optind++]);
273 fprintf(stderr, "\n");
274 return STATUS_ERROR_EXIT;
275 }
276
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300277 return STATUS_OK;
Nathan Scott36389fb2021-03-23 15:04:54 +1100278}
279
Nathan Scott36756112021-04-08 09:26:48 +1000280static void CommandLine_delay(ProcessList* pl, unsigned long millisec) {
Nathan Scott36389fb2021-03-23 15:04:54 +1100281 struct timespec req = {
282 .tv_sec = 0,
283 .tv_nsec = millisec * 1000000L
284 };
Nathan Scott36756112021-04-08 09:26:48 +1000285 while (nanosleep(&req, &req) == -1)
Nathan Scott36389fb2021-03-23 15:04:54 +1100286 continue;
Nathan Scott36756112021-04-08 09:26:48 +1000287 Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
Nathan Scott36389fb2021-03-23 15:04:54 +1100288}
289
290static void setCommFilter(State* state, char** commFilter) {
291 ProcessList* pl = state->pl;
292 IncSet* inc = state->mainPanel->inc;
293
294 IncSet_setFilter(inc, *commFilter);
295 pl->incFilter = IncSet_filter(inc);
296
297 free(*commFilter);
298 *commFilter = NULL;
299}
300
Nathan Scott14da84f2023-04-04 16:47:11 +1000301int CommandLine_run(int argc, char** argv) {
Nathan Scott36389fb2021-03-23 15:04:54 +1100302
303 /* initialize locale */
304 const char* lc_ctype;
305 if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL")))
306 setlocale(LC_CTYPE, lc_ctype);
307 else
308 setlocale(LC_CTYPE, "");
309
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300310 CommandLineStatus status = STATUS_OK;
311 CommandLineSettings flags = { 0 };
312
Nathan Scott14da84f2023-04-04 16:47:11 +1000313 if ((status = parseArguments(argc, argv, &flags)) != STATUS_OK)
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300314 return status != STATUS_OK_EXIT ? 1 : 0;
Nathan Scott36389fb2021-03-23 15:04:54 +1100315
Christian Göttsche36880cd2021-01-21 20:27:37 +0100316 if (flags.readonly)
317 Settings_enableReadonly();
318
Volodymyr Vasiutyk2ef70ad2021-10-06 10:45:07 +0300319 if (!Platform_init())
320 return 1;
Nathan Scott36389fb2021-03-23 15:04:54 +1100321
322 Process_setupColumnWidths();
323
324 UsersTable* ut = UsersTable_new();
Nathan Scottf0ed0fd2021-06-23 17:44:56 +1000325 Hashtable* dm = DynamicMeters_new();
Nathan Scotte7f447b2023-04-04 16:24:37 +1000326 Hashtable* dc = DynamicColumns_new();
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200327 if (!dc)
328 dc = Hashtable_new(0, true);
Nathan Scott36389fb2021-03-23 15:04:54 +1100329
Nathan Scotte7f447b2023-04-04 16:24:37 +1000330 ProcessList* pl = ProcessList_new(ut, flags.pidMatchList, flags.userId);
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200331
Nathan Scotte7f447b2023-04-04 16:24:37 +1000332 Settings* settings = Settings_new(pl->activeCPUs, dm, dc);
Nathan Scott36389fb2021-03-23 15:04:54 +1100333 pl->settings = settings;
334
335 Header* header = Header_new(pl, settings, 2);
336
337 Header_populateFromSettings(header);
338
339 if (flags.delay != -1)
340 settings->delay = flags.delay;
341 if (!flags.useColors)
342 settings->colorScheme = COLORSCHEME_MONOCHROME;
niae8f27eb2021-07-14 21:07:43 +0200343#ifdef HAVE_GETMOUSE
Nathan Scott36389fb2021-03-23 15:04:54 +1100344 if (!flags.enableMouse)
345 settings->enableMouse = false;
niae8f27eb2021-07-14 21:07:43 +0200346#endif
Nathan Scott36389fb2021-03-23 15:04:54 +1100347 if (flags.treeView)
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000348 settings->ss->treeView = true;
Nathan Scott36389fb2021-03-23 15:04:54 +1100349 if (flags.highlightChanges)
350 settings->highlightChanges = true;
351 if (flags.highlightDelaySecs != -1)
352 settings->highlightDelaySecs = flags.highlightDelaySecs;
353 if (flags.sortKey > 0) {
354 // -t -s <key> means "tree sorted by key"
355 // -s <key> means "list sorted by key" (previous existing behavior)
356 if (!flags.treeView) {
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000357 settings->ss->treeView = false;
Nathan Scott36389fb2021-03-23 15:04:54 +1100358 }
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000359 ScreenSettings_setSortKey(settings->ss, flags.sortKey);
Nathan Scott36389fb2021-03-23 15:04:54 +1100360 }
361
362 CRT_init(settings, flags.allowUnicode);
363
364 MainPanel* panel = MainPanel_new();
365 ProcessList_setPanel(pl, (Panel*) panel);
366
Daniel Langec6f946e2022-03-25 14:51:14 +0100367 MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter);
Nathan Scott36389fb2021-03-23 15:04:54 +1100368
369 State state = {
370 .settings = settings,
371 .ut = ut,
372 .pl = pl,
373 .mainPanel = panel,
374 .header = header,
375 .pauseProcessUpdate = false,
376 .hideProcessSelection = false,
Christian Göttsche15fe50d2022-10-20 15:03:17 +0200377 .hideMeters = false,
Nathan Scott36389fb2021-03-23 15:04:54 +1100378 };
379
380 MainPanel_setState(panel, &state);
381 if (flags.commFilter)
382 setCommFilter(&state, &(flags.commFilter));
383
384 ScreenManager* scr = ScreenManager_new(header, settings, &state, true);
385 ScreenManager_add(scr, (Panel*) panel, -1);
386
387 ProcessList_scan(pl, false);
Nathan Scott36756112021-04-08 09:26:48 +1000388 CommandLine_delay(pl, 75);
Nathan Scott36389fb2021-03-23 15:04:54 +1100389 ProcessList_scan(pl, false);
390
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000391 if (settings->ss->allBranchesCollapsed)
Nathan Scott36389fb2021-03-23 15:04:54 +1100392 ProcessList_collapseAllBranches(pl);
393
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000394 ScreenManager_run(scr, NULL, NULL, NULL);
Nathan Scott36389fb2021-03-23 15:04:54 +1100395
Nathan Scott36389fb2021-03-23 15:04:54 +1100396 Platform_done();
397
398 CRT_done();
399
400 if (settings->changed) {
Christian Göttsche1f5f40c2021-05-16 19:55:31 +0200401 int r = Settings_write(settings, false);
Nathan Scott36389fb2021-03-23 15:04:54 +1100402 if (r < 0)
403 fprintf(stderr, "Can not save configuration to %s: %s\n", settings->filename, strerror(-r));
404 }
405
406 Header_delete(header);
407 ProcessList_delete(pl);
408
409 ScreenManager_delete(scr);
410 MetersPanel_cleanup();
411
412 UsersTable_delete(ut);
Nathan Scott36389fb2021-03-23 15:04:54 +1100413
414 if (flags.pidMatchList)
415 Hashtable_delete(flags.pidMatchList);
416
Christian Göttsche68460b22021-08-14 19:52:26 +0200417 CRT_resetSignalHandlers();
418
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200419 /* Delete these last, since they can get accessed in the crash handler */
Christian Göttsche1f5f40c2021-05-16 19:55:31 +0200420 Settings_delete(settings);
Nathan Scottc0c2bb92021-09-03 12:11:31 +1000421 DynamicColumns_delete(dc);
422 DynamicMeters_delete(dm);
Christian Göttsche1f5f40c2021-05-16 19:55:31 +0200423
Nathan Scott36389fb2021-03-23 15:04:54 +1100424 return 0;
425}