Settings.c 12.9 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
9
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#include "Settings.h"

Hisham Muhammad's avatar
Hisham Muhammad committed
10
11
#include "String.h"
#include "Vector.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12

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

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
24
25
26
27
28
29
typedef struct {
   int len;
   char** names;
   int* modes;
} MeterColumnSettings;
   
Hisham Muhammad's avatar
Hisham Muhammad committed
30
typedef struct Settings_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
31
32
33
34
35
36
   char* filename;
   
   MeterColumnSettings columns[2];

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

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
65
66
}*/

Hisham Muhammad's avatar
Hisham Muhammad committed
67
68
69
70
static ProcessField defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };

//static ProcessField defaultIoFields[] = { PID, IO_PRIORITY, USER, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, COMM, 0 };

Hisham Muhammad's avatar
Hisham Muhammad committed
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
}

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

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

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

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

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