Settings.c 12.8 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
/*
htop - Settings.c
Hisham Muhammad's avatar
Hisham Muhammad committed
3
(C) 2004-2011 Hisham H. Muhammad
Hisham Muhammad's avatar
Hisham Muhammad committed
4
5
6
7
8
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#include "Settings.h"
9
#include "Platform.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
10

Hisham Muhammad's avatar
Hisham Muhammad committed
11
12
#include "String.h"
#include "Vector.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
13
#include "CRT.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
14

Hisham Muhammad's avatar
Hisham Muhammad committed
15
16
17
18
19
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

Hisham Muhammad's avatar
Hisham Muhammad committed
20
21
22
#define DEFAULT_DELAY 15

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
23
#include "Process.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
24
#include <stdbool.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
25

Hisham Muhammad's avatar
Hisham Muhammad committed
26
27
28
29
30
typedef struct {
   int len;
   char** names;
   int* modes;
} MeterColumnSettings;
31

Hisham Muhammad's avatar
Hisham Muhammad committed
32
typedef struct Settings_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
33
34
35
36
37
38
   char* filename;
   
   MeterColumnSettings columns[2];

   ProcessField* fields;
   int flags;
Hisham Muhammad's avatar
Hisham Muhammad committed
39
40
   int colorScheme;
   int delay;
Hisham Muhammad's avatar
Hisham Muhammad committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

   int direction;
   ProcessField sortKey;

   bool countCPUsFromZero;
   bool detailedCPUTime;
   bool treeView;
   bool hideThreads;
   bool shadowOtherUsers;
   bool showThreadNames;
   bool hideKernelThreads;
   bool hideUserlandThreads;
   bool highlightBaseName;
   bool highlightMegabytes;
   bool highlightThreads;
   bool updateProcessNames;
   bool accountGuestInCPUMeter;
   bool headerMargin;

Hisham Muhammad's avatar
Hisham Muhammad committed
60
   bool changed;
Hisham Muhammad's avatar
Hisham Muhammad committed
61
62
} Settings;

Hisham Muhammad's avatar
Hisham Muhammad committed
63
64
65
66
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif

Hisham Muhammad's avatar
Hisham Muhammad committed
67
68
69
}*/

void Settings_delete(Settings* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
70
71
72
73
74
75
   free(this->filename);
   free(this->fields);
   for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
      String_freeArray(this->columns[i].names);
      free(this->columns[i].modes);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
76
77
78
   free(this);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
79
static void Settings_readMeters(Settings* this, char* line, int column) {
Hisham Muhammad's avatar
Hisham Muhammad committed
80
   char* trim = String_trim(line);
81
82
   int nIds;
   char** ids = String_split(trim, ' ', &nIds);
Hisham Muhammad's avatar
Hisham Muhammad committed
83
   free(trim);
Hisham Muhammad's avatar
Hisham Muhammad committed
84
   this->columns[column].names = ids;
Hisham Muhammad's avatar
Hisham Muhammad committed
85
86
}

Hisham Muhammad's avatar
Hisham Muhammad committed
87
static void Settings_readMeterModes(Settings* this, char* line, int column) {
Hisham Muhammad's avatar
Hisham Muhammad committed
88
   char* trim = String_trim(line);
89
90
   int nIds;
   char** ids = String_split(trim, ' ', &nIds);
Hisham Muhammad's avatar
Hisham Muhammad committed
91
   free(trim);
Hisham Muhammad's avatar
Hisham Muhammad committed
92
   int len = 0;
93
   for (int i = 0; ids[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
94
95
96
97
98
99
      len++;
   }
   this->columns[column].len = len;
   int* modes = calloc(len, sizeof(int));
   for (int i = 0; i < len; i++) {
      modes[i] = atoi(ids[i]);
Hisham Muhammad's avatar
Hisham Muhammad committed
100
101
   }
   String_freeArray(ids);
Hisham Muhammad's avatar
Hisham Muhammad committed
102
   this->columns[column].modes = modes;
Hisham Muhammad's avatar
Hisham Muhammad committed
103
104
}

Hisham Muhammad's avatar
Hisham Muhammad committed
105
106
107
108
109
110
static void Settings_defaultMeters(Settings* this, int cpuCount) {
   int sizes[] = { 3, 3 };
   if (cpuCount > 4) {
      sizes[1]++;
   }
   for (int i = 0; i < 2; i++) {
111
      this->columns[i].names = calloc(sizes[i] + 1, sizeof(char*));
Hisham Muhammad's avatar
Hisham Muhammad committed
112
      this->columns[i].modes = calloc(sizes[i], sizeof(int));
113
      this->columns[i].len = sizes[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
114
115
116
   }
   
   int r = 0;
117
   if (cpuCount > 8) {
Hisham Muhammad's avatar
Hisham Muhammad committed
118
119
      this->columns[0].names[0] = strdup("LeftCPUs2");
      this->columns[1].names[r++] = strdup("RightCPUs2");
120
   } else if (cpuCount > 4) {
Hisham Muhammad's avatar
Hisham Muhammad committed
121
122
      this->columns[0].names[0] = strdup("LeftCPUs");
      this->columns[1].names[r++] = strdup("RightCPUs");
123
   } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
      this->columns[0].names[0] = strdup("AllCPUs");
   }
   this->columns[0].names[1] = strdup("Memory");
   this->columns[0].names[2] = strdup("Swap");
   
   this->columns[1].names[r++] = strdup("Tasks");
   this->columns[1].names[r++] = strdup("LoadAverage");
   this->columns[1].names[r++] = strdup("Uptime");
}

static void readFields(ProcessField* fields, int* flags, const char* line) {
   char* trim = String_trim(line);
   int nIds;
   char** ids = String_split(trim, ' ', &nIds);
   free(trim);
   int i, j;
   *flags = 0;
141
   for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
142
143
      // This "+1" is for compatibility with the older enum format.
      int id = atoi(ids[i]) + 1;
144
      if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
Hisham Muhammad's avatar
Hisham Muhammad committed
145
146
147
148
         fields[j] = id;
         *flags |= Process_fields[id].flags;
         j++;
      }
149
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
150
151
   fields[j] = (ProcessField) NULL;
   String_freeArray(ids);
152
153
}

154
static bool Settings_read(Settings* this, const char* fileName, int cpuCount) {
155
156
   FILE* fd = fopen(fileName, "r");
   if (!fd)
Hisham Muhammad's avatar
Hisham Muhammad committed
157
      return false;
158
   
Hisham Muhammad's avatar
Hisham Muhammad committed
159
   const int maxLine = 2048;
Hisham Muhammad's avatar
Hisham Muhammad committed
160
161
   char buffer[maxLine];
   bool readMeters = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
162
   while (fgets(buffer, maxLine, fd)) {
163
164
165
166
167
168
      int nOptions;
      char** option = String_split(buffer, '=', &nOptions);
      if (nOptions < 2) {
         String_freeArray(option);
         continue;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
169
      if (String_eq(option[0], "fields")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
170
         readFields(this->fields, &(this->flags), option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
171
172
      } else if (String_eq(option[0], "sort_key")) {
         // This "+1" is for compatibility with the older enum format.
Hisham Muhammad's avatar
Hisham Muhammad committed
173
         this->sortKey = atoi(option[1]) + 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
174
      } else if (String_eq(option[0], "sort_direction")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
175
         this->direction = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
176
      } else if (String_eq(option[0], "tree_view")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
177
         this->treeView = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
178
      } else if (String_eq(option[0], "hide_threads")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
179
         this->hideThreads = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
180
      } else if (String_eq(option[0], "hide_kernel_threads")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
181
         this->hideKernelThreads = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
182
      } else if (String_eq(option[0], "hide_userland_threads")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
183
         this->hideUserlandThreads = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
184
      } else if (String_eq(option[0], "shadow_other_users")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
185
         this->shadowOtherUsers = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
186
      } else if (String_eq(option[0], "show_thread_names")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
187
         this->showThreadNames = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
188
      } else if (String_eq(option[0], "highlight_base_name")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
189
         this->highlightBaseName = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
190
      } else if (String_eq(option[0], "highlight_megabytes")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
191
         this->highlightMegabytes = atoi(option[1]);
192
      } else if (String_eq(option[0], "highlight_threads")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
193
         this->highlightThreads = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
194
      } else if (String_eq(option[0], "header_margin")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
195
         this->headerMargin = atoi(option[1]);
196
      } else if (String_eq(option[0], "expand_system_time")) {
197
         // Compatibility option.
Hisham Muhammad's avatar
Hisham Muhammad committed
198
         this->detailedCPUTime = atoi(option[1]);
199
      } else if (String_eq(option[0], "detailed_cpu_time")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
200
         this->detailedCPUTime = atoi(option[1]);
201
      } else if (String_eq(option[0], "cpu_count_from_zero")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
202
         this->countCPUsFromZero = atoi(option[1]);
203
      } else if (String_eq(option[0], "update_process_names")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
204
         this->updateProcessNames = atoi(option[1]);
205
      } else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
206
         this->accountGuestInCPUMeter = atoi(option[1]);
Hisham Muhammad's avatar
Hisham Muhammad committed
207
208
209
210
211
      } else if (String_eq(option[0], "delay")) {
         this->delay = atoi(option[1]);
      } else if (String_eq(option[0], "color_scheme")) {
         this->colorScheme = atoi(option[1]);
         if (this->colorScheme < 0) this->colorScheme = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
212
         if (this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = LAST_COLORSCHEME - 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
213
      } else if (String_eq(option[0], "left_meters")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
214
         Settings_readMeters(this, option[1], 0);
215
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
216
      } else if (String_eq(option[0], "right_meters")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
217
         Settings_readMeters(this, option[1], 1);
218
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
219
      } else if (String_eq(option[0], "left_meter_modes")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
220
         Settings_readMeterModes(this, option[1], 0);
221
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
222
      } else if (String_eq(option[0], "right_meter_modes")) {
Hisham Muhammad's avatar
Hisham Muhammad committed
223
         Settings_readMeterModes(this, option[1], 1);
224
         readMeters = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
225
226
227
228
229
      }
      String_freeArray(option);
   }
   fclose(fd);
   if (!readMeters) {
Hisham Muhammad's avatar
Hisham Muhammad committed
230
      Settings_defaultMeters(this, cpuCount);
Hisham Muhammad's avatar
Hisham Muhammad committed
231
232
233
234
   }
   return true;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
   fprintf(fd, "%s=", name);
   for (int i = 0; fields[i]; i++) {
      // This "-1" is for compatibility with the older enum format.
      fprintf(fd, "%d ", (int) fields[i]-1);
   }
   fprintf(fd, "\n");
}

static void writeMeters(Settings* this, FILE* fd, int column) {
   for (int i = 0; i < this->columns[column].len; i++) {
      fprintf(fd, "%s ", this->columns[column].names[i]);
   }
   fprintf(fd, "\n");
}

static void writeMeterModes(Settings* this, FILE* fd, int column) {
   for (int i = 0; i < this->columns[column].len; i++) {
      fprintf(fd, "%d ", this->columns[column].modes[i]);
   }
   fprintf(fd, "\n");
}

Hisham Muhammad's avatar
Hisham Muhammad committed
258
259
bool Settings_write(Settings* this) {
   FILE* fd;
Hisham Muhammad's avatar
Hisham Muhammad committed
260
   fd = fopen(this->filename, "w");
Hisham Muhammad's avatar
Hisham Muhammad committed
261
262
263
   if (fd == NULL) {
      return false;
   }
264
   fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
265
   fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
266
   writeFields(fd, this->fields, "fields");
Hisham Muhammad's avatar
Hisham Muhammad committed
267
   // This "-1" is for compatibility with the older enum format.
Hisham Muhammad's avatar
Hisham Muhammad committed
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
   fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
   fprintf(fd, "sort_direction=%d\n", (int) this->direction);
   fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
   fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
   fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
   fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
   fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames);
   fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
   fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
   fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
   fprintf(fd, "tree_view=%d\n", (int) this->treeView);
   fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
   fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
   fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
   fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
   fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
Hisham Muhammad's avatar
Hisham Muhammad committed
284
285
   fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
   fprintf(fd, "delay=%d\n", (int) this->delay);
Hisham Muhammad's avatar
Hisham Muhammad committed
286
287
288
289
   fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
   fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
   fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
   fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
290
291
292
   fclose(fd);
   return true;
}
293

Hisham Muhammad's avatar
Hisham Muhammad committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
Settings* Settings_new(int cpuCount) {
  
   Settings* this = calloc(1, sizeof(Settings));

   this->sortKey = PERCENT_CPU;
   this->direction = 1;
   this->hideThreads = false;
   this->shadowOtherUsers = false;
   this->showThreadNames = false;
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
   this->detailedCPUTime = false;
   this->countCPUsFromZero = false;
   this->updateProcessNames = false;
   
312
   this->fields = calloc(Platform_numberOfFields+1, sizeof(ProcessField));
Hisham Muhammad's avatar
Hisham Muhammad committed
313
314
315
   // TODO: turn 'fields' into a Vector,
   // (and ProcessFields into proper objects).
   this->flags = 0;
316
   ProcessField* defaults = Platform_defaultFields;
Hisham Muhammad's avatar
Hisham Muhammad committed
317
318
319
320
321
   for (int i = 0; defaults[i]; i++) {
      this->fields[i] = defaults[i];
      this->flags |= Process_fields[defaults[i]].flags;
   }

322
323
324
   char* legacyDotfile = NULL;
   char* rcfile = getenv("HTOPRC");
   if (rcfile) {
Hisham Muhammad's avatar
Hisham Muhammad committed
325
      this->filename = strdup(rcfile);
326
327
328
329
330
331
332
   } else {
      const char* home = getenv("HOME");
      if (!home) home = "";
      const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
      char* configDir = NULL;
      char* htopDir = NULL;
      if (xdgConfigHome) {
Hisham Muhammad's avatar
Hisham Muhammad committed
333
         this->filename = String_cat(xdgConfigHome, "/htop/htoprc");
334
335
336
         configDir = strdup(xdgConfigHome);
         htopDir = String_cat(xdgConfigHome, "/htop");
      } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
337
         this->filename = String_cat(home, "/.config/htop/htoprc");
338
339
340
341
         configDir = String_cat(home, "/.config");
         htopDir = String_cat(home, "/.config/htop");
      }
      legacyDotfile = String_cat(home, "/.htoprc");
342
343
      (void) mkdir(configDir, 0700);
      (void) mkdir(htopDir, 0700);
344
345
      free(htopDir);
      free(configDir);
346
      struct stat st;
347
348
349
      if (lstat(legacyDotfile, &st) != 0) {
         st.st_mode = 0;
      }
350
      if (access(legacyDotfile, R_OK) != 0 || S_ISLNK(st.st_mode)) {
351
352
353
354
         free(legacyDotfile);
         legacyDotfile = NULL;
      }
   }
355
356
357
   this->colorScheme = 0;
   this->changed = false;
   this->delay = DEFAULT_DELAY;
Hisham Muhammad's avatar
Hisham Muhammad committed
358
   bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->filename, cpuCount);
359
360
361
362
363
364
365
   if (ok) {
      if (legacyDotfile) {
         // Transition to new location and delete old configuration file
         if (Settings_write(this))
            unlink(legacyDotfile);
      }
   } else {
366
367
368
      this->changed = true;
      // TODO: how to get SYSCONFDIR correctly through Autoconf?
      char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
369
      ok = Settings_read(this, systemSettings, cpuCount);
370
371
      free(systemSettings);
      if (!ok) {
Hisham Muhammad's avatar
Hisham Muhammad committed
372
373
374
375
         Settings_defaultMeters(this, cpuCount);
         this->hideKernelThreads = true;
         this->highlightMegabytes = true;
         this->highlightThreads = false;
376
         this->headerMargin = true;
377
378
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
379
   free(legacyDotfile);
380
381
   return this;
}
Hisham Muhammad's avatar
Hisham Muhammad committed
382
383
384
385
386
387
388

void Settings_invertSortOrder(Settings* this) {
   if (this->direction == 1)
      this->direction = -1;
   else
      this->direction = 1;
}