1 #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__FreeBSD_kernel__) && !defined(__DragonFly__)
2 #if defined(__clang__)
3 #pragma clang diagnostic push
4 #pragma clang diagnostic ignored "-Wreserved-id-macro"
5 #endif
6 #define _XOPEN_SOURCE 600
7 #if defined(__clang__)
8 #pragma clang diagnostic pop
9 #endif
10 #endif
11 /* enable wcswidth on kFreeBSD */
12 #if defined(__FreeBSD_kernel__) && defined(__GLIBC__)
13 #define __USE_XOPEN
14 #define _XOPEN_SOURCE
15 #endif
16 #include "common.h"
17 #include "misc.h"
18 #include <wchar.h>
19
spacecheck(const char * path)20 int spacecheck(const char *path)
21 {
22 struct statvfs buf;
23 uint64_t free;
24
25 /* do space check only when configured for it */
26 if (!cfg.spacecheck) {
27 return 1;
28 }
29
30 if (statvfs(path, &buf)) {
31 if (noexit) {
32 return 0;
33 } else {
34 snprintf(errorstring, 1024, "Free diskspace check failed: %s", strerror(errno));
35 printe(PT_Error);
36 exit(EXIT_FAILURE);
37 }
38 }
39
40 free = (uint64_t)((double)buf.f_bavail / (double)1024) * buf.f_bsize;
41
42 if (debug) {
43 printf("bsize %d\n", (int)buf.f_bsize);
44 printf("blocks %lu\n", (unsigned long int)buf.f_blocks);
45 printf("bfree %lu\n", (unsigned long int)buf.f_bfree);
46 printf("bavail %lu\n", (unsigned long int)buf.f_bavail);
47 printf("ffree %lu\n", (unsigned long int)buf.f_ffree);
48 printf("%" PRIu64 " free space left\n", free);
49 }
50
51 /* the database is likely to be less than 200 kiB but let's require */
52 /* 1 MiB to be on the safe side, anyway, the filesystem should */
53 /* always have more free space than that */
54 if (free <= 1024) {
55 return 0;
56 } else {
57 return 1;
58 }
59 }
60
sighandler(int sig)61 void sighandler(int sig)
62 {
63 /* set signal */
64 intsignal = sig;
65
66 if (debug) {
67 switch (sig) {
68
69 case SIGHUP:
70 snprintf(errorstring, 1024, "DEBUG: SIGHUP (%d)", sig);
71 break;
72
73 case SIGTERM:
74 snprintf(errorstring, 1024, "DEBUG: SIGTERM (%d)", sig);
75 break;
76
77 case SIGINT:
78 snprintf(errorstring, 1024, "DEBUG: SIGINT (%d)", sig);
79 break;
80
81 default:
82 snprintf(errorstring, 1024, "DEBUG: Unknown signal %d", sig);
83 break;
84 }
85 printe(PT_Info);
86 }
87 }
88
getbtime(void)89 uint64_t getbtime(void)
90 {
91 uint64_t result = 0;
92 #if defined(__linux__)
93 FILE *fp;
94 int check;
95 char temp[64], statline[128];
96
97 if ((fp = fopen("/proc/stat", "r")) == NULL) {
98 snprintf(errorstring, 1024, "Unable to read /proc/stat: %s", strerror(errno));
99 printe(PT_Error);
100 if (noexit) {
101 return 0;
102 } else {
103 exit(1);
104 }
105 }
106
107 check = 0;
108 while (fgets(statline, 128, fp) != NULL) {
109 sscanf(statline, "%63s", temp);
110 if (strcmp(temp, "btime") == 0) {
111 /* if (debug)
112 printf("\n%s\n",statline); */
113 check = 1;
114 break;
115 }
116 }
117 fclose(fp);
118
119 if (check == 0) {
120 snprintf(errorstring, 1024, "btime missing from /proc/stat.");
121 printe(PT_Error);
122 if (noexit) {
123 return 0;
124 } else {
125 exit(1);
126 }
127 }
128
129 result = strtoull(statline + 6, (char **)NULL, 0);
130
131 #elif defined(BSD_VNSTAT)
132 struct timeval btm;
133 size_t len = sizeof(btm);
134 int mib[2] = {CTL_KERN, KERN_BOOTTIME};
135
136 if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) {
137 if (debug)
138 printf("sysctl(kern.boottime) failed.\n");
139 return 0;
140 }
141
142 result = (uint64_t)btm.tv_sec;
143 #endif
144
145 return result;
146 }
147
getvalue(const uint64_t bytes,const int len,const RequestType type)148 char *getvalue(const uint64_t bytes, const int len, const RequestType type)
149 {
150 static char buffer[64];
151 int i, declen = cfg.defaultdecimals, p = 1024;
152 uint64_t limit;
153
154 if (type == RT_ImageScale) {
155 declen = 0;
156 }
157
158 if (cfg.unitmode == 2) {
159 p = 1000;
160 }
161
162 if ((type == RT_Estimate) && (bytes == 0)) {
163 declen = len - (int)strlen(getunitprefix(2)) - 2;
164 if (declen < 2) {
165 declen = 2;
166 }
167 snprintf(buffer, 64, "%*s %*s", declen, "--", (int)strlen(getunitprefix(2)), " ");
168 } else {
169 for (i = UNITPREFIXCOUNT - 1; i > 0; i--) {
170 limit = (uint64_t)(pow(p, i - 1)) * 1000;
171 if (bytes >= limit) {
172 if (i > 1) {
173 snprintf(buffer, 64, "%" DECCONV "*.*f %s", getunitspacing(len, 5), declen, (double)bytes / (double)(getunitdivisor(cfg.unitmode, i + 1)), getunitprefix(i + 1));
174 } else {
175 if (type == RT_Estimate) {
176 declen = 0;
177 }
178 snprintf(buffer, 64, "%" DECCONV "*.*f %s", getunitspacing(len, 2), declen, (double)bytes / (double)(getunitdivisor(cfg.unitmode, i + 1)), getunitprefix(i + 1));
179 }
180 return buffer;
181 }
182 }
183 snprintf(buffer, 64, "%" DECCONV "*" PRIu64 " %s", getunitspacing(len, 1), bytes, getunitprefix(1));
184 }
185
186 return buffer;
187 }
188
getunitspacing(const int len,const int index)189 int getunitspacing(const int len, const int index)
190 {
191 int l = len;
192
193 /* tune spacing according to unit */
194 /* +1 for space between number and unit */
195 l -= (int)strlen(getunitprefix(index)) + 1;
196 if (l < 0) {
197 l = 1;
198 }
199
200 return l;
201 }
202
gettrafficrate(const uint64_t bytes,const time_t interval,const int len)203 char *gettrafficrate(const uint64_t bytes, const time_t interval, const int len)
204 {
205 static char buffer[64];
206 int declen = cfg.defaultdecimals;
207 uint64_t b = bytes;
208
209 if (interval == 0) {
210 snprintf(buffer, 64, "%*s", len, "n/a");
211 return buffer;
212 }
213
214 /* convert to proper unit */
215 if (cfg.rateunit == 1) {
216 b *= 8;
217 }
218
219 return getratestring(b / (uint64_t)interval, len, declen);
220 }
221
getunitprefix(const int index)222 const char *getunitprefix(const int index)
223 {
224 /* clang-format off */
225 static const char *unitprefix[] = { "na",
226 "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", /* IEC - 1024^n */
227 "B", "KB", "MB", "GB", "TB", "PB", "EB", /* JEDEC - 1024^n */
228 "B", "kB", "MB", "GB", "TB", "PB", "EB" }; /* SI - 1000^n */
229 /* clang-format on */
230
231 if (index > UNITPREFIXCOUNT) {
232 return unitprefix[0];
233 } else {
234 return unitprefix[(cfg.unitmode * UNITPREFIXCOUNT) + index];
235 }
236 }
237
getrateunitprefix(const int unitmode,const int index)238 const char *getrateunitprefix(const int unitmode, const int index)
239 {
240 /* clang-format off */
241 static const char *rateunitprefix[] = { "na",
242 "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", "PiB/s", "EiB/s", /* IEC - 1024^n */
243 "B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s", "EB/s", /* JEDEC - 1024^n */
244 "B/s", "kB/s", "MB/s", "GB/s", "TB/s", "PB/s", "EB/s", /* SI - 1000^n */
245 "bit/s", "Kibit/s", "Mibit/s", "Gibit/s", "Tibit/s", "Pibit/s", "Eibit/s", /* IEC - 1024^n */
246 "bit/s", "kbit/s", "Mbit/s", "Gbit/s", "Tbit/s", "Pbit/s", "Ebit/s" }; /* SI - 1000^n */
247 /* clang-format on */
248
249 if (index > UNITPREFIXCOUNT) {
250 return rateunitprefix[0];
251 } else {
252 return rateunitprefix[(unitmode * UNITPREFIXCOUNT) + index];
253 }
254 }
255
getunitdivisor(const int unitmode,const int index)256 uint64_t getunitdivisor(const int unitmode, const int index)
257 {
258 if (index > UNITPREFIXCOUNT) {
259 return 1;
260 } else {
261 if (unitmode == 2 || unitmode == 4) {
262 return (uint64_t)(pow(1000, index - 1));
263 } else {
264 return (uint64_t)(pow(1024, index - 1));
265 }
266 }
267 }
268
getunit(void)269 int getunit(void)
270 {
271 int unit;
272
273 if (cfg.rateunit == 0) {
274 unit = cfg.unitmode;
275 } else {
276 unit = 3 + cfg.rateunitmode;
277 }
278
279 return unit;
280 }
281
getratestring(const uint64_t rate,const int len,const int declen)282 char *getratestring(const uint64_t rate, const int len, const int declen)
283 {
284 int l, i, unit, p = 1024;
285 static char buffer[64];
286 uint64_t limit;
287
288 unit = getunit();
289
290 if (unit == 2 || unit == 4) {
291 p = 1000;
292 }
293
294 for (i = UNITPREFIXCOUNT - 1; i > 0; i--) {
295 limit = (uint64_t)(pow(p, i - 1)) * 1000;
296 if (rate >= limit) {
297 l = getratespacing(len, unit, i + 1);
298 snprintf(buffer, 64, "%" DECCONV "*.*f %s", l, declen, (double)rate / (double)(getunitdivisor(unit, i + 1)), getrateunitprefix(unit, i + 1));
299 return buffer;
300 }
301 }
302
303 l = getratespacing(len, unit, 1);
304 snprintf(buffer, 64, "%" DECCONV "*.0f %s", l, (double)rate / (double)(getunitdivisor(unit, 1)), getrateunitprefix(unit, 1));
305 return buffer;
306 }
307
getratespacing(const int len,const int unitmode,const int unitindex)308 int getratespacing(const int len, const int unitmode, const int unitindex)
309 {
310 int l = len;
311
312 l -= (int)strlen(getrateunitprefix(unitmode, unitindex)) + 1;
313 if (l < 0) {
314 l = 1;
315 }
316
317 return l;
318 }
319
getpadding(const int len,const char * str)320 int getpadding(const int len, const char *str)
321 {
322 #if defined(HAVE_MBSTOWCS) && defined(HAVE_WCSWIDTH)
323 wchar_t wbuffer[64];
324 if (!cfg.utflocale) {
325 return len;
326 }
327 if ((int)mbstowcs(wbuffer, str, 64) < 0) {
328 return len;
329 }
330 return len + ((int)strlen(str) - wcswidth(wbuffer, 64));
331 #else
332 return len;
333 #endif
334 }
335
cursortocolumn(const int column)336 void cursortocolumn(const int column)
337 {
338 printf("\033[%dG", column);
339 }
340
cursorhide(void)341 void cursorhide(void)
342 {
343 printf("\033[?25l");
344 }
345
cursorshow(void)346 void cursorshow(void)
347 {
348 printf("\033[?25h");
349 }
350
eraseline(void)351 void eraseline(void)
352 {
353 printf("\033[2K");
354 }
355
356 /* validity of date or time itself isn't checked here as sqlite handles that */
validatedatetime(const char * str)357 int validatedatetime(const char *str)
358 {
359 short valid;
360 unsigned int len, i, t;
361 const char *templates[] = {"dddd-dd-dd dd:dd", "dddd-dd-dd"};
362
363 len = (unsigned int)strlen(str);
364 if (len > strlen(templates[0])) {
365 return 0;
366 }
367
368 for (t = 0; t < 2; t++) {
369 if (len != strlen(templates[t])) {
370 continue;
371 }
372 valid = 1;
373 for (i = 0; i < strlen(templates[t]); i++) {
374 switch (templates[t][i]) {
375 case 'd':
376 if (!isdigit(str[i])) {
377 valid = 0;
378 }
379 break;
380 default:
381 if (str[i] != templates[t][i]) {
382 valid = 0;
383 }
384 break;
385 }
386 if (!valid) {
387 break;
388 }
389 }
390 if (!valid) {
391 continue;
392 }
393 return 1;
394 }
395
396 return 0;
397 }
398
issametimeslot(const ListType listtype,const time_t entry,const time_t updated)399 int issametimeslot(const ListType listtype, const time_t entry, const time_t updated)
400 {
401 struct tm e, u;
402
403 if (updated < entry) {
404 return 0;
405 }
406
407 if (entry == updated) {
408 return 1;
409 }
410
411 if (localtime_r(&entry, &e) == NULL || localtime_r(&updated, &u) == NULL) {
412 return 0;
413 }
414
415 switch (listtype) {
416 case LT_5min:
417 if ((entry - (entry % 300)) == (updated - (updated % 300))) {
418 return 1;
419 }
420 break;
421 case LT_Hour:
422 if (e.tm_year == u.tm_year && e.tm_yday == u.tm_yday && e.tm_hour == u.tm_hour) {
423 return 1;
424 }
425 break;
426 case LT_Top:
427 case LT_Day:
428 if (e.tm_year == u.tm_year && e.tm_yday == u.tm_yday) {
429 return 1;
430 }
431 break;
432 case LT_Month:
433 if (e.tm_year == u.tm_year && e.tm_mon == u.tm_mon) {
434 return 1;
435 }
436 break;
437 case LT_Year:
438 if (e.tm_year == u.tm_year) {
439 return 1;
440 }
441 break;
442 case LT_None:
443 return 0;
444 }
445
446 return 0;
447 }
448
getperiodseconds(const ListType listtype,const time_t entry,const time_t updated,const short isongoing)449 uint64_t getperiodseconds(const ListType listtype, const time_t entry, const time_t updated, const short isongoing)
450 {
451 struct tm e, u;
452 uint64_t seconds = 0;
453
454 if (localtime_r(&entry, &e) == NULL || localtime_r(&updated, &u) == NULL) {
455 return 0;
456 }
457
458 if (isongoing) {
459 if (listtype == LT_Day) {
460 seconds = (uint64_t)u.tm_sec + (uint64_t)u.tm_min * 60 + (uint64_t)u.tm_hour * 3600;
461 } else if (listtype == LT_Month) {
462 seconds = (uint64_t)mosecs(entry, updated);
463 } else if (listtype == LT_Year) {
464 seconds = (uint64_t)u.tm_yday * 86400 + (uint64_t)u.tm_sec + (uint64_t)u.tm_min * 60 + (uint64_t)u.tm_hour * 3600;
465 } else if (listtype == LT_Top) {
466 seconds = 86400;
467 } else if (listtype == LT_Hour) {
468 seconds = (uint64_t)u.tm_sec + (uint64_t)u.tm_min * 60;
469 } else if (listtype == LT_5min) {
470 seconds = (uint64_t)u.tm_sec + (uint64_t)u.tm_min % 5 * 60;
471 }
472 } else {
473 if (listtype == LT_Day || listtype == LT_Top) {
474 seconds = 86400;
475 } else if (listtype == LT_Month) {
476 seconds = (uint64_t)dmonth(e.tm_mon) * 86400;
477 } else if (listtype == LT_Year) {
478 seconds = (uint64_t)(365 + isleapyear(e.tm_year + 1900)) * 86400;
479 } else if (listtype == LT_Hour) {
480 seconds = 3600;
481 } else if (listtype == LT_5min) {
482 seconds = 300;
483 }
484 }
485
486 return seconds;
487 }
488
getestimates(uint64_t * rx,uint64_t * tx,const ListType listtype,const time_t updated,dbdatalist ** dbdata)489 void getestimates(uint64_t *rx, uint64_t *tx, const ListType listtype, const time_t updated, dbdatalist **dbdata)
490 {
491 struct tm u;
492 uint64_t div = 0, mult = 0;
493 dbdatalist *datalist_i = *dbdata;
494
495 *rx = *tx = 0;
496
497 if (datalist_i == NULL) {
498 return;
499 }
500
501 if (localtime_r(&updated, &u) == NULL) {
502 return;
503 }
504
505 /* last entry on the list is the most recent entry */
506 while (datalist_i->next != NULL) {
507 datalist_i = datalist_i->next;
508 }
509
510 if (datalist_i->rx == 0 || datalist_i->tx == 0) {
511 return;
512 }
513
514 /* LT_5min and LT_Hour don't have the estimate line visible in outputs */
515 /* but are used by BarColumnShowsRate which requires "past" values for */
516 /* full hours / 5 minutes for the bar to show correctly */
517 if (listtype == LT_5min) {
518 div = ((uint64_t)u.tm_min % 5 * 60) + (uint64_t)u.tm_sec;
519 if (div == 0) {
520 div = 1;
521 mult = 1;
522 } else {
523 mult = 300;
524 }
525 } else if (listtype == LT_Hour) {
526 div = (uint64_t)u.tm_min * 60 + (uint64_t)u.tm_sec;
527 if (div == 0) {
528 div = 1;
529 mult = 1;
530 } else {
531 mult = 3600;
532 }
533 } else if (listtype == LT_Day) {
534 div = (uint64_t)u.tm_hour * 3600 + (uint64_t)u.tm_min * 60 + (uint64_t)u.tm_sec;
535 mult = 86400;
536 } else if (listtype == LT_Month) {
537 div = (uint64_t)mosecs(datalist_i->timestamp, updated);
538 mult = (uint64_t)dmonth(u.tm_mon) * 86400;
539 } else if (listtype == LT_Year) {
540 div = (uint64_t)u.tm_yday * 1440 + (uint64_t)u.tm_hour * 60 + (uint64_t)u.tm_min;
541 mult = (uint64_t)(365 + isleapyear(u.tm_year + 1900)) * 1440;
542 }
543 if (div > 0) {
544 *rx = (uint64_t)((double)datalist_i->rx / (double)div) * mult;
545 *tx = (uint64_t)((double)datalist_i->tx / (double)div) * mult;
546 }
547 }
548
ishelprequest(const char * arg)549 int ishelprequest(const char *arg)
550 {
551 if (strlen(arg) == 0) {
552 return 0;
553 }
554
555 if (strlen(arg) == 1 && arg[0] == '?') {
556 return 1;
557 } else if ((strcmp(arg, "-?") == 0) || (strcmp(arg, "--help") == 0)) {
558 return 1;
559 }
560
561 return 0;
562 }
563