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