1 /* From wmload.c, v0.9.2, licensed under the GPL. */
2 #include <config.h>
3 #include <sys/types.h>
4 #include <sys/statvfs.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 
8 #include <glibtop.h>
9 #include <glibtop/cpu.h>
10 #include <glibtop/mem.h>
11 #include <glibtop/swap.h>
12 #include <glibtop/loadavg.h>
13 #include <glibtop/netload.h>
14 #include <glibtop/netlist.h>
15 #include <glibtop/mountlist.h>
16 #include <glibtop/fsusage.h>
17 
18 #include "linux-proc.h"
19 #include "autoscaler.h"
20 
21 static const unsigned needed_cpu_flags =
22 (1 << GLIBTOP_CPU_USER) +
23 (1 << GLIBTOP_CPU_IDLE) +
24 (1 << GLIBTOP_CPU_SYS) +
25 (1 << GLIBTOP_CPU_NICE);
26 
27 #if 0
28 static const unsigned needed_page_flags =
29 (1 << GLIBTOP_SWAP_PAGEIN) +
30 (1 << GLIBTOP_SWAP_PAGEOUT);
31 #endif
32 
33 static const unsigned needed_mem_flags =
34 (1 << GLIBTOP_MEM_USED) +
35 (1 << GLIBTOP_MEM_FREE);
36 
37 static const unsigned needed_swap_flags =
38 (1 << GLIBTOP_SWAP_USED) +
39 (1 << GLIBTOP_SWAP_FREE);
40 
41 static const unsigned needed_loadavg_flags =
42 (1 << GLIBTOP_LOADAVG_LOADAVG);
43 
44 static const unsigned needed_netload_flags =
45 (1 << GLIBTOP_NETLOAD_IF_FLAGS) +
46 (1 << GLIBTOP_NETLOAD_BYTES_TOTAL);
47 
48 void
GetLoad(guint64 Maximum,guint64 data[cpuload_n],LoadGraph * g)49 GetLoad (guint64    Maximum,
50          guint64    data [cpuload_n],
51          LoadGraph *g)
52 {
53     MultiloadApplet *multiload;
54     glibtop_cpu cpu;
55     guint64 cpu_aux [cpuload_n], used = 0, total = 0;
56     guint64 current_scaled, used_scaled = 0;
57     unsigned i;
58 
59     glibtop_get_cpu (&cpu);
60 
61     g_return_if_fail ((cpu.flags & needed_cpu_flags) == needed_cpu_flags);
62 
63     multiload = g->multiload;
64 
65     multiload->cpu_time [cpuload_usr]    = cpu.user;
66     multiload->cpu_time [cpuload_nice]   = cpu.nice;
67     multiload->cpu_time [cpuload_sys]    = cpu.sys;
68     multiload->cpu_time [cpuload_iowait] = cpu.iowait + cpu.irq + cpu.softirq;
69     multiload->cpu_time [cpuload_free]   = cpu.idle;
70 
71     if (!multiload->cpu_initialized) {
72         memcpy (multiload->cpu_last, multiload->cpu_time, sizeof (multiload->cpu_last));
73         multiload->cpu_initialized = TRUE;
74     }
75 
76     for (i = 0; i < cpuload_n; i++) {
77         cpu_aux [i] = multiload->cpu_time [i] - multiload->cpu_last [i];
78         total += cpu_aux [i];
79     }
80 
81     for (i = 0; i < cpuload_free; i++) {
82         used += cpu_aux [i];
83         current_scaled = (guint64) ((float)(cpu_aux [i] * Maximum) / (float)total);
84         used_scaled += current_scaled;
85         data [i] = current_scaled;
86     }
87     data [cpuload_free] = Maximum - used_scaled;
88 
89     multiload->cpu_used_ratio = (float)(used) / (float)total;
90 
91     memcpy (multiload->cpu_last, multiload->cpu_time, sizeof multiload->cpu_last);
92 }
93 
94 void
GetDiskLoad(guint64 Maximum,guint64 data[diskload_n],LoadGraph * g)95 GetDiskLoad (guint64    Maximum,
96              guint64    data [diskload_n],
97              LoadGraph *g)
98 {
99     static gboolean first_call = TRUE;
100     static guint64 lastread = 0, lastwrite = 0;
101     static AutoScaler scaler;
102 
103     guint64 max;
104     guint64 read, write;
105     guint64 readdiff, writediff;
106     guint   i;
107 
108     MultiloadApplet *multiload;
109 
110     multiload = g->multiload;
111 
112 
113     if(first_call)
114     {
115         autoscaler_init (&scaler, g->speed, 500);
116     }
117 
118     read = write = 0;
119 
120     if (multiload->nvme_diskstats)
121     {
122         FILE *fdr;
123         char line[255];
124         guint64 s_read, s_write;
125 
126         fdr = fopen("/proc/diskstats", "r");
127         if (!fdr)
128         {
129             multiload->nvme_diskstats = FALSE;
130             g_settings_set_boolean (multiload->settings, "diskload-nvme-diskstats", FALSE);
131             return;
132         }
133 
134         while (fgets(line, 255, fdr))
135         {
136             /* Match main device, rather than individual partitions (e.g. nvme0n1) */
137             if (!g_regex_match_simple("\\snvme\\d+\\w+\\d+\\s", line, 0, 0))
138             {
139                 continue;
140             }
141 
142             /*
143                6 - sectors read
144                10 - sectors written
145                */
146             if (sscanf(line, "%*d %*d %*s %*d %*d %ld %*d %*d %*d %ld", &s_read, &s_write) == 2)
147             {
148                 read += 512 * s_read;
149                 write += 512 * s_write;
150             }
151         }
152         fclose(fdr);
153     }
154     else
155     {
156         glibtop_mountlist mountlist;
157         glibtop_mountentry *mountentries;
158 
159         mountentries = glibtop_get_mountlist (&mountlist, FALSE);
160 
161         for (i = 0; i < mountlist.number; i++)
162         {
163             struct statvfs statresult;
164             glibtop_fsusage fsusage;
165 
166             if (strstr (mountentries[i].devname, "/dev/") == NULL)
167                 continue;
168 
169             if (strstr (mountentries[i].mountdir, "/media/") != NULL)
170                 continue;
171 
172             if (statvfs (mountentries[i].mountdir, &statresult) < 0)
173             {
174                 g_debug ("Failed to get statistics for mount entry: %s. Reason: %s. Skipping entry.",
175                          mountentries[i].mountdir, strerror(errno));
176                 continue;
177             }
178 
179             glibtop_get_fsusage(&fsusage, mountentries[i].mountdir);
180             read += fsusage.read;
181             write += fsusage.write;
182         }
183 
184         g_free(mountentries);
185     }
186 
187     readdiff  = read - lastread;
188     writediff = write - lastwrite;
189 
190     lastread  = read;
191     lastwrite = write;
192 
193     if(first_call)
194     {
195         first_call = FALSE;
196         memset(data, 0, 3 * sizeof data[0]);
197         return;
198     }
199 
200     max = autoscaler_get_max(&scaler, readdiff + writediff);
201 
202     multiload->diskload_used_ratio = (float)(readdiff + writediff) / (float)max;
203 
204     data [diskload_read]  = (guint64) ((float)Maximum *  (float)readdiff / (float)max);
205     data [diskload_write] = (guint64) ((float)Maximum * (float)writediff / (float)max);
206     data [diskload_free]  = Maximum - (data [0] + data[1]);
207 }
208 
209 /* GNU/Linux:
210  *   aux [memload_user]   = (mem.total - mem.free) - (mem.cached + mem.buffer)
211  *   aux [memload_shared] = mem.shared;
212  *   aux [memload_cached] = mem.cached - mem.shared;
213  *   aux [memload_buffer] = mem.buffer;
214  *
215  * Other operating systems:
216  *   aux [memload_user]   = mem.user;
217  *   aux [memload_shared] = mem.shared;
218  *   aux [memload_cached] = mem.cached;
219  *   aux [memload_buffer] = mem.buffer;
220  */
221 void
GetMemory(guint64 Maximum,guint64 data[memload_n],LoadGraph * g)222 GetMemory (guint64    Maximum,
223            guint64    data [memload_n],
224            LoadGraph *g)
225 {
226     MultiloadApplet *multiload;
227     glibtop_mem mem;
228     guint64 aux [memload_n], cache = 0;
229     guint64 current_scaled, used_scaled = 0;
230     int i;
231 
232     glibtop_get_mem (&mem);
233 
234     g_return_if_fail ((mem.flags & needed_mem_flags) == needed_mem_flags);
235 
236 #ifndef __linux__
237     aux [memload_user]   = mem.user;
238     aux [memload_cached] = mem.cached;
239 #else
240     aux [memload_user]   = mem.total - mem.free - mem.buffer - mem.cached;;
241     aux [memload_cached] = mem.cached - mem.shared;
242 #endif /* __linux__ */
243     aux [memload_shared] = mem.shared;
244     aux [memload_buffer] = mem.buffer;
245 
246     for (i = 0; i < memload_free; i++) {
247         current_scaled = (guint64) ((float)(aux [i] * Maximum) / (float)mem.total);
248         if (i != memload_user) {
249             cache += aux [i];
250         }
251         used_scaled += current_scaled;
252         data [i] = current_scaled;
253     }
254     data [memload_free] = MAX (Maximum - used_scaled, 0);
255 
256     multiload = g->multiload;
257     multiload->memload_user  = aux [memload_user];
258     multiload->memload_cache = cache;
259     multiload->memload_total = mem.total;
260 }
261 
262 void
GetSwap(guint64 Maximum,guint64 data[swapload_n],LoadGraph * g)263 GetSwap (guint64    Maximum,
264          guint64    data [swapload_n],
265          LoadGraph *g)
266 {
267     guint64 used;
268     MultiloadApplet *multiload;
269     glibtop_swap swap;
270 
271     glibtop_get_swap (&swap);
272     g_return_if_fail ((swap.flags & needed_swap_flags) == needed_swap_flags);
273 
274     multiload = g->multiload;
275 
276     if (swap.total == 0) {
277         used = 0;
278         multiload->swapload_used_ratio = 0.0f;
279     }
280     else {
281         float ratio;
282 
283         ratio = (float)swap.used / (float)swap.total;
284         used = (guint64) ((float) Maximum * ratio);
285         multiload->swapload_used_ratio = ratio;
286     }
287 
288     data [0] = used;
289     data [1] = Maximum - used;
290 }
291 
292 void
GetLoadAvg(guint64 Maximum,guint64 data[2],LoadGraph * g)293 GetLoadAvg (guint64    Maximum,
294             guint64    data [2],
295             LoadGraph *g)
296 {
297     glibtop_loadavg loadavg;
298     MultiloadApplet *multiload;
299 
300     glibtop_get_loadavg (&loadavg);
301 
302     g_return_if_fail ((loadavg.flags & needed_loadavg_flags) == needed_loadavg_flags);
303 
304     multiload = g->multiload;
305     multiload->loadavg1 = loadavg.loadavg[0];
306 
307     data [0] = (guint64) ((float) Maximum * loadavg.loadavg[0]);
308     data [1] = Maximum - data[0];
309 }
310 
311 /*
312  * Return true if a network device (identified by its name) is virtual
313  * (ie: not corresponding to a physical device). In case it is a physical
314  * device or unknown, returns false.
315  */
316 static gboolean
is_net_device_virtual(char * device)317 is_net_device_virtual(char *device)
318 {
319     /*
320      * There is not definitive way to find out. On some systems (Linux
321      * kernels ≳ 2.19 without option SYSFS_DEPRECATED), there exist a
322      * directory /sys/devices/virtual/net which only contains virtual
323      * devices.  It's also possible to detect by the fact that virtual
324      * devices do not have a symlink "device" in
325      * /sys/class/net/name-of-dev/ .  This second method is more complex
326      * but more reliable.
327      */
328     gboolean ret = FALSE;
329     char *path = malloc (strlen (device) + strlen ("/sys/class/net//device") + 1);
330 
331     if (path == NULL)
332         return FALSE;
333 
334     /* Check if /sys/class/net/name-of-dev/ exists (may be old linux kernel
335      * or not linux at all). */
336     do {
337         if (sprintf(path, "/sys/class/net/%s", device) < 0)
338             break;
339         if (access(path, F_OK) != 0)
340             break; /* unknown */
341 
342         if (sprintf(path, "/sys/class/net/%s/device", device) < 0)
343             break;
344         if (access(path, F_OK) != 0)
345             ret = TRUE;
346     } while (0);
347 
348     free (path);
349     return ret;
350 }
351 
352 void
GetNet(guint64 Maximum,guint64 data[4],LoadGraph * g)353 GetNet (guint64    Maximum,
354         guint64    data [4],
355         LoadGraph *g)
356 {
357     enum Types {
358         IN_COUNT = 0,
359         OUT_COUNT = 1,
360         LOCAL_COUNT = 2,
361         COUNT_TYPES = 3
362     };
363 
364     static int ticks = 0;
365     static guint64 past[COUNT_TYPES] = {0};
366 
367     guint64 present[COUNT_TYPES] = {0};
368 
369     guint i;
370     gchar **devices;
371     glibtop_netlist netlist;
372 
373     MultiloadApplet *multiload;
374 
375     multiload = g->multiload;
376     devices = glibtop_get_netlist(&netlist);
377 
378     for(i = 0; i < netlist.number; ++i)
379     {
380         glibtop_netload netload;
381 
382         glibtop_get_netload(&netload, devices[i]);
383 
384         g_return_if_fail((netload.flags & needed_netload_flags) == needed_netload_flags);
385 
386         if (!(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP)))
387             continue;
388 
389         if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) {
390             /* for loopback in and out are identical, so only count in */
391             present[LOCAL_COUNT] += netload.bytes_in;
392             continue;
393         }
394 
395         /*
396          * Do not include virtual devices (VPN, PPPOE...) to avoid
397          * counting the same throughput several times.
398          */
399         if (is_net_device_virtual(devices[i]))
400             continue;
401 
402         present[IN_COUNT] += netload.bytes_in;
403         present[OUT_COUNT] += netload.bytes_out;
404     }
405 
406     g_strfreev(devices);
407     netspeed_add (multiload->netspeed_in, present[IN_COUNT]);
408     netspeed_add (multiload->netspeed_out, present[OUT_COUNT]);
409 
410     if(ticks < 2) /* avoid initial spike */
411     {
412         ticks++;
413         memset(data, 0, (COUNT_TYPES + 1) * sizeof data[0]);
414     }
415     else
416     {
417         data[COUNT_TYPES] = 0;
418         float seconds = (float) g->speed / 1000.0f;
419         for (i = 0; i < COUNT_TYPES; i++)
420         {
421             /* protect against weirdness */
422             if (present[i] >= past[i])
423                 data[i] = (guint) ((float) (present[i] - past[i]) / seconds);
424             else
425                 data[i] = 0;
426             data[COUNT_TYPES] += data[i];
427         }
428     }
429 
430     memcpy(past, present, sizeof past);
431 }
432