Settings.c 13 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

David Hunt's avatar
David Hunt committed
11
#include "StringUtils.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12
#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
   int cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
43
44
45
46
47
48
   int direction;
   ProcessField sortKey;

   bool countCPUsFromZero;
   bool detailedCPUTime;
   bool treeView;
49
   bool showProgramPath;
Hisham Muhammad's avatar
Hisham Muhammad committed
50
51
52
53
54
55
56
57
58
59
60
61
   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
62
   bool changed;
Hisham Muhammad's avatar
Hisham Muhammad committed
63
64
} Settings;

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

Hisham Muhammad's avatar
Hisham Muhammad committed
69
70
71
}*/

void Settings_delete(Settings* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
72
73
74
75
76
77
   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
78
79
80
   free(this);
}

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

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

107
static void Settings_defaultMeters(Settings* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
108
   int sizes[] = { 3, 3 };
109
   if (this->cpuCount > 4) {
Hisham Muhammad's avatar
Hisham Muhammad committed
110
111
112
      sizes[1]++;
   }
   for (int i = 0; i < 2; i++) {
113
      this->columns[i].names = calloc(sizes[i] + 1, sizeof(char*));
Hisham Muhammad's avatar
Hisham Muhammad committed
114
      this->columns[i].modes = calloc(sizes[i], sizeof(int));
115
      this->columns[i].len = sizes[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
116
117
118
   }
   
   int r = 0;
119
   if (this->cpuCount > 8) {
Hisham Muhammad's avatar
Hisham Muhammad committed
120
121
      this->columns[0].names[0] = strdup("LeftCPUs2");
      this->columns[1].names[r++] = strdup("RightCPUs2");
122
   } else if (this->cpuCount > 4) {
Hisham Muhammad's avatar
Hisham Muhammad committed
123
124
      this->columns[0].names[0] = strdup("LeftCPUs");
      this->columns[1].names[r++] = strdup("RightCPUs");
125
   } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
      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;
143
   for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
144
145
      // This "+1" is for compatibility with the older enum format.
      int id = atoi(ids[i]) + 1;
146
      if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
Hisham Muhammad's avatar
Hisham Muhammad committed
147
148
149
150
         fields[j] = id;
         *flags |= Process_fields[id].flags;
         j++;
      }
151
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
152
153
   fields[j] = (ProcessField) NULL;
   String_freeArray(ids);
154
155
}

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

Hisham Muhammad's avatar
Hisham Muhammad committed
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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
261
262
bool Settings_write(Settings* this) {
   FILE* fd;
Hisham Muhammad's avatar
Hisham Muhammad committed
263
   fd = fopen(this->filename, "w");
Hisham Muhammad's avatar
Hisham Muhammad committed
264
265
266
   if (fd == NULL) {
      return false;
   }
267
   fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
268
   fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
269
   writeFields(fd, this->fields, "fields");
Hisham Muhammad's avatar
Hisham Muhammad committed
270
   // This "-1" is for compatibility with the older enum format.
Hisham Muhammad's avatar
Hisham Muhammad committed
271
272
273
274
275
276
277
   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);
278
   fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath);
Hisham Muhammad's avatar
Hisham Muhammad committed
279
280
281
282
283
284
285
286
287
   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
288
289
   fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
   fprintf(fd, "delay=%d\n", (int) this->delay);
Hisham Muhammad's avatar
Hisham Muhammad committed
290
291
292
293
   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
294
295
296
   fclose(fd);
   return true;
}
297

Hisham Muhammad's avatar
Hisham Muhammad committed
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
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;
315
   this->cpuCount = cpuCount;
316
   this->showProgramPath = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
317
   
318
   this->fields = calloc(Platform_numberOfFields+1, sizeof(ProcessField));
Hisham Muhammad's avatar
Hisham Muhammad committed
319
320
321
   // TODO: turn 'fields' into a Vector,
   // (and ProcessFields into proper objects).
   this->flags = 0;
322
   ProcessField* defaults = Platform_defaultFields;
Hisham Muhammad's avatar
Hisham Muhammad committed
323
324
325
326
327
   for (int i = 0; defaults[i]; i++) {
      this->fields[i] = defaults[i];
      this->flags |= Process_fields[defaults[i]].flags;
   }

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

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