Settings.c 13.3 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
      len++;
   }
   this->columns[column].len = len;
Hisham's avatar
Hisham committed
99
   int* modes = xCalloc(len, sizeof(int));
Hisham Muhammad's avatar
Hisham Muhammad committed
100
101
   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++) {
Hisham's avatar
Hisham committed
113
114
      this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
      this->columns[i].modes = xCalloc(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's avatar
Hisham committed
120
121
      this->columns[0].names[0] = xStrdup("LeftCPUs2");
      this->columns[1].names[r++] = xStrdup("RightCPUs2");
122
   } else if (this->cpuCount > 4) {
Hisham's avatar
Hisham committed
123
124
      this->columns[0].names[0] = xStrdup("LeftCPUs");
      this->columns[1].names[r++] = xStrdup("RightCPUs");
125
   } else {
Hisham's avatar
Hisham committed
126
      this->columns[0].names[0] = xStrdup("AllCPUs");
Hisham Muhammad's avatar
Hisham Muhammad committed
127
   }
Hisham's avatar
Hisham committed
128
129
   this->columns[0].names[1] = xStrdup("Memory");
   this->columns[0].names[2] = xStrdup("Swap");
Hisham Muhammad's avatar
Hisham Muhammad committed
130
   
Hisham's avatar
Hisham committed
131
132
133
   this->columns[1].names[r++] = xStrdup("Tasks");
   this->columns[1].names[r++] = xStrdup("LoadAverage");
   this->columns[1].names[r++] = xStrdup("Uptime");
Hisham Muhammad's avatar
Hisham Muhammad committed
134
135
136
137
138
139
140
141
142
}

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
159
160
   FILE* fd;
   uid_t euid = geteuid();

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

Hisham Muhammad's avatar
Hisham Muhammad committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
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
266
267
bool Settings_write(Settings* this) {
   FILE* fd;
268
269
270
   uid_t euid = geteuid();

   seteuid(getuid());
Hisham Muhammad's avatar
Hisham Muhammad committed
271
   fd = fopen(this->filename, "w");
272
   seteuid(euid);
Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
   if (fd == NULL) {
      return false;
   }
276
   fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
277
   fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
Hisham Muhammad's avatar
Hisham Muhammad committed
278
   writeFields(fd, this->fields, "fields");
Hisham Muhammad's avatar
Hisham Muhammad committed
279
   // This "-1" is for compatibility with the older enum format.
Hisham Muhammad's avatar
Hisham Muhammad committed
280
281
282
283
284
285
286
   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);
287
   fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath);
Hisham Muhammad's avatar
Hisham Muhammad committed
288
289
290
291
292
293
294
295
296
   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
297
298
   fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
   fprintf(fd, "delay=%d\n", (int) this->delay);
Hisham Muhammad's avatar
Hisham Muhammad committed
299
300
301
302
   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
303
304
305
   fclose(fd);
   return true;
}
306

Hisham Muhammad's avatar
Hisham Muhammad committed
307
308
Settings* Settings_new(int cpuCount) {
  
Hisham's avatar
Hisham committed
309
   Settings* this = xCalloc(1, sizeof(Settings));
Hisham Muhammad's avatar
Hisham Muhammad committed
310
311
312
313
314
315
316
317
318
319
320
321
322
323

   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;
324
   this->cpuCount = cpuCount;
325
   this->showProgramPath = true;
Hisham's avatar
Hisham committed
326
   this->highlightThreads = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
327
   
Hisham's avatar
Hisham committed
328
   this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
Hisham Muhammad's avatar
Hisham Muhammad committed
329
330
331
   // TODO: turn 'fields' into a Vector,
   // (and ProcessFields into proper objects).
   this->flags = 0;
332
   ProcessField* defaults = Platform_defaultFields;
Hisham Muhammad's avatar
Hisham Muhammad committed
333
334
335
336
337
   for (int i = 0; defaults[i]; i++) {
      this->fields[i] = defaults[i];
      this->flags |= Process_fields[defaults[i]].flags;
   }

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

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