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

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

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

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

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

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

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

   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
59
   bool changed;
Hisham Muhammad's avatar
Hisham Muhammad committed
60
61
} Settings;

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

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

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

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

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

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

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

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

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

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

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