1 #include "common.h"
2 #include "cfg.h"
3 
loadcfg(const char * cfgfile,const ConfigType type)4 int loadcfg(const char *cfgfile, const ConfigType type)
5 {
6 	FILE *fd;
7 	int i;
8 	unsigned int linelen, cfglen;
9 
10 	char value[512], cfgline[512];
11 
12 	struct cfgsetting cset[] =
13 		{/* cfg string, char var name, int var name, char len, fill status */
14 		 {"Interface", cfg.iface, 0, 32, 0},
15 		 {"DatabaseDir", cfg.dbdir, 0, 512, 0},
16 		 {"Locale", cfg.locale, 0, 32, 0},
17 		 {"MonthRotate", 0, &cfg.monthrotate, 0, 0},
18 		 {"MonthRotateAffectsYears", 0, &cfg.monthrotateyears, 0, 0},
19 		 {"DayFormat", cfg.dformat, 0, 64, 0},
20 		 {"MonthFormat", cfg.mformat, 0, 64, 0},
21 		 {"TopFormat", cfg.tformat, 0, 64, 0},
22 		 {"RXCharacter", cfg.rxchar, 0, 2, 0},
23 		 {"TXCharacter", cfg.txchar, 0, 2, 0},
24 		 {"RXHourCharacter", cfg.rxhourchar, 0, 2, 0},
25 		 {"TXHourCharacter", cfg.txhourchar, 0, 2, 0},
26 		 {"UnitMode", 0, &cfg.unitmode, 0, 0},
27 		 {"RateUnitMode", 0, &cfg.rateunitmode, 0, 0},
28 		 {"OutputStyle", 0, &cfg.ostyle, 0, 0},
29 		 {"EstimateBarVisible", 0, &cfg.estimatebarvisible, 0, 0},
30 		 {"RateUnit", 0, &cfg.rateunit, 0, 0},
31 		 {"DefaultDecimals", 0, &cfg.defaultdecimals, 0, 0},
32 		 {"HourlyDecimals", 0, &cfg.hourlydecimals, 0, 0},
33 		 {"HourlySectionStyle", 0, &cfg.hourlystyle, 0, 0},
34 		 {"BandwidthDetection", 0, &cfg.bwdetection, 0, 0},
35 		 {"MaxBandwidth", 0, &cfg.maxbw, 0, 0},
36 		 {"Sampletime", 0, &cfg.sampletime, 0, 0},
37 		 {"QueryMode", 0, &cfg.qmode, 0, 0},
38 		 {"CheckDiskSpace", 0, &cfg.spacecheck, 0, 0},
39 		 {"BootVariation", 0, &cfg.bvar, 0, 0},
40 		 {"TrafficlessEntries", 0, &cfg.trafficlessentries, 0, 0},
41 		 {"List5Mins", 0, &cfg.listfivemins, 0, 0},
42 		 {"ListHours", 0, &cfg.listhours, 0, 0},
43 		 {"ListDays", 0, &cfg.listdays, 0, 0},
44 		 {"ListMonths", 0, &cfg.listmonths, 0, 0},
45 		 {"ListYears", 0, &cfg.listyears, 0, 0},
46 		 {"ListTop", 0, &cfg.listtop, 0, 0},
47 		 {"5MinuteHours", 0, &cfg.fiveminutehours, 0, 0},
48 		 {"HourlyDays", 0, &cfg.hourlydays, 0, 0},
49 		 {"DailyDays", 0, &cfg.dailydays, 0, 0},
50 		 {"MonthlyMonths", 0, &cfg.monthlymonths, 0, 0},
51 		 {"YearlyYears", 0, &cfg.yearlyyears, 0, 0},
52 		 {"TopDayEntries", 0, &cfg.topdayentries, 0, 0},
53 		 {"DaemonUser", cfg.daemonuser, 0, 33, 0},
54 		 {"DaemonGroup", cfg.daemongroup, 0, 33, 0},
55 		 {"TimeSyncWait", 0, &cfg.timesyncwait, 0, 0},
56 		 {"UpdateInterval", 0, &cfg.updateinterval, 0, 0},
57 		 {"PollInterval", 0, &cfg.pollinterval, 0, 0},
58 		 {"SaveInterval", 0, &cfg.saveinterval, 0, 0},
59 		 {"OfflineSaveInterval", 0, &cfg.offsaveinterval, 0, 0},
60 		 {"AlwaysAddNewInterfaces", 0, &cfg.alwaysadd, 0, 0},
61 		 {"BandwidthDetectionInterval", 0, &cfg.bwdetectioninterval, 0, 0},
62 		 {"SaveOnStatusChange", 0, &cfg.savestatus, 0, 0},
63 		 {"UseLogging", 0, &cfg.uselogging, 0, 0},
64 		 {"CreateDirs", 0, &cfg.createdirs, 0, 0},
65 		 {"UpdateFileOwner", 0, &cfg.updatefileowner, 0, 0},
66 		 {"LogFile", cfg.logfile, 0, 512, 0},
67 		 {"PidFile", cfg.pidfile, 0, 512, 0},
68 		 {"64bitInterfaceCounters", 0, &cfg.is64bit, 0, 0},
69 		 {"DatabaseWriteAheadLogging", 0, &cfg.waldb, 0, 0},
70 		 {"DatabaseSynchronous", 0, &cfg.dbsynchronous, 0, 0},
71 		 {"UseUTC", 0, &cfg.useutc, 0, 0},
72 		 {"HeaderFormat", cfg.hformat, 0, 64, 0},
73 		 {"HourlyRate", 0, &cfg.hourlyrate, 0, 0},
74 		 {"SummaryRate", 0, &cfg.summaryrate, 0, 0},
75 		 {"TransparentBg", 0, &cfg.transbg, 0, 0},
76 		 {"LargeFonts", 0, &cfg.largefonts, 0, 0},
77 		 {"LineSpacingAdjustment", 0, &cfg.linespaceadjust, 0, 0},
78 		 {"ImageScale", 0, &cfg.imagescale, 0, 0},
79 		 {"5MinuteGraphResultCount", 0, &cfg.fivegresultcount, 0, 0},
80 		 {"5MinuteGraphHeight", 0, &cfg.fivegheight, 0, 0},
81 		 {"SummaryGraph", 0, &cfg.summarygraph, 0, 0},
82 		 {"EstimateStyle", 0, &cfg.estimatestyle, 0, 0},
83 		 {"BarColumnShowsRate", 0, &cfg.barshowsrate, 0, 0},
84 		 {"CBackground", cfg.cbg, 0, 8, 0},
85 		 {"CEdge", cfg.cedge, 0, 8, 0},
86 		 {"CHeader", cfg.cheader, 0, 8, 0},
87 		 {"CHeaderTitle", cfg.cheadertitle, 0, 8, 0},
88 		 {"CHeaderDate", cfg.cheaderdate, 0, 8, 0},
89 		 {"CText", cfg.ctext, 0, 8, 0},
90 		 {"CLine", cfg.cline, 0, 8, 0},
91 		 {"CLineL", cfg.clinel, 0, 8, 0},
92 		 {"CRx", cfg.crx, 0, 8, 0},
93 		 {"CRxD", cfg.crxd, 0, 8, 0},
94 		 {"CTx", cfg.ctx, 0, 8, 0},
95 		 {"CTxD", cfg.ctxd, 0, 8, 0},
96 		 {"Experimental", 0, &cfg.experimental, 0, 0},
97 		 {0, 0, 0, 0, 0}};
98 
99 	/* load default config */
100 	defaultcfg();
101 
102 	i = opencfgfile(cfgfile, &fd);
103 	if (i != 2)
104 		return i;
105 
106 	rewind(fd);
107 
108 	/* parse every config file line */
109 	while (!feof(fd)) {
110 
111 		cfgline[0] = '\0';
112 		if (fgets(cfgline, 512, fd) == NULL) {
113 			break;
114 		}
115 
116 		linelen = (unsigned int)strlen(cfgline);
117 		if (linelen <= 2 || cfgline[0] == '#') {
118 			continue;
119 		}
120 
121 		for (i = 0; cset[i].name != 0; i++) {
122 
123 			if (cset[i].found) {
124 				continue;
125 			}
126 
127 			cfglen = (unsigned int)strlen(cset[i].name);
128 			if ((linelen < (cfglen + 2)) || (strncasecmp(cfgline, cset[i].name, cfglen) != 0)) {
129 				continue;
130 			}
131 
132 			if (!extractcfgvalue(value, 512, cfgline, cfglen)) {
133 				if (debug)
134 					printf("  c: %s   -> \"%s\" with no value, keeping default.\n", cfgline, cset[i].name);
135 				cset[i].found = 1;
136 				break;
137 			}
138 
139 			if (!setcfgvalue(&cset[i], value, cfgline)) {
140 				continue;
141 			}
142 
143 			cset[i].found = 1;
144 			break;
145 		}
146 
147 		if ((debug) && (!cset[i].found) && (strncasecmp(cfgline, "MaxBW", 5) != 0))
148 			printf("Unknown configuration line: %s", cfgline);
149 	}
150 
151 	fclose(fd);
152 
153 	/* validate config */
154 	validatecfg(type);
155 
156 	return 1;
157 }
158 
validatebool(const char * cfgname,int32_t * cfgptr,const int32_t defaultvalue)159 void validatebool(const char *cfgname, int32_t *cfgptr, const int32_t defaultvalue)
160 {
161 	validateint(cfgname, cfgptr, defaultvalue, 0, 1);
162 }
163 
validateint(const char * cfgname,int32_t * cfgptr,const int32_t defaultvalue,const int32_t minvalue,const int32_t maxvalue)164 void validateint(const char *cfgname, int32_t *cfgptr, const int32_t defaultvalue, const int32_t minvalue, const int32_t maxvalue)
165 {
166 	if (maxvalue > minvalue) {
167 		if (*cfgptr < minvalue || *cfgptr > maxvalue) {
168 			snprintf(errorstring, 1024, "Invalid value \"%d\" for %s. Value needs to be between %d and %d. Using default value %d.", *cfgptr, cfgname, minvalue, maxvalue, defaultvalue);
169 			printe(PT_Config);
170 			*cfgptr = defaultvalue;
171 		}
172 	} else {
173 		if (*cfgptr < minvalue) {
174 			snprintf(errorstring, 1024, "Invalid value \"%d\" for %s. Value needs to be at least %d. Using default value %d.", *cfgptr, cfgname, minvalue, defaultvalue);
175 			printe(PT_Config);
176 			*cfgptr = defaultvalue;
177 		}
178 	}
179 }
180 
validatecfg(const ConfigType type)181 void validatecfg(const ConfigType type)
182 {
183 	uint32_t rolloversecs;
184 	const char *invalidvalue = "Invalid value for";
185 	const char *resettingto = "using default value";
186 	const char *noslashstart = "doesn't start with \"/\", using default value instead.";
187 
188 	validateint("UnitMode", &cfg.unitmode, UNITMODE, 0, 2);
189 	validatebool("RateUnitMode", &cfg.rateunitmode, RATEUNITMODE);
190 	validateint("OutputStyle", &cfg.ostyle, OSTYLE, 0, 3);
191 	validatebool("EstimateBarVisible", &cfg.estimatebarvisible, ESTIMATEBARVISIBLE);
192 	validateint("DefaultDecimals", &cfg.defaultdecimals, DEFAULTDECIMALS, 0, 2);
193 	validateint("HourlyDecimals", &cfg.hourlydecimals, HOURLYDECIMALS, 0, 2);
194 	validateint("HourlySectionStyle", &cfg.hourlystyle, HOURLYSTYLE, 0, 3);
195 	validateint("BootVariation", &cfg.bvar, BVAR, 0, 300);
196 	validateint("Sampletime", &cfg.sampletime, DEFSAMPTIME, 2, 600);
197 	validateint("MonthRotate", &cfg.monthrotate, MONTHROTATE, 1, 28);
198 	validatebool("MonthRotateAffectsYears", &cfg.monthrotateyears, MONTHROTATEYEARS);
199 	validateint("MaxBandwidth", &cfg.maxbw, DEFMAXBW, 0, BWMAX);
200 	validatebool("CheckDiskSpace", &cfg.spacecheck, USESPACECHECK);
201 	validateint("TimeSyncWait", &cfg.timesyncwait, TIMESYNCWAIT, 0, 60);
202 	validateint("PollInterval", &cfg.pollinterval, POLLINTERVAL, 2, 60);
203 	validatebool("SaveOnStatusChange", &cfg.savestatus, SAVESTATUS);
204 	validateint("UseLogging", &cfg.uselogging, USELOGGING, 0, 2);
205 	validateint("CreateDirs", &cfg.createdirs, CREATEDIRS, 0, 2);
206 	validateint("UpdateFileOwner", &cfg.updatefileowner, UPDATEFILEOWNER, 0, 2);
207 	validateint("64bitInterfaceCounters", &cfg.is64bit, IS64BIT, -2, 1);
208 	validatebool("DatabaseWriteAheadLogging", &cfg.waldb, WALDB);
209 	validateint("DatabaseSynchronous", &cfg.dbsynchronous, DBSYNCHRONOUS, -1, 3);
210 	validatebool("UseUTC", &cfg.useutc, USEUTC);
211 	if (type == CT_Image || type == CT_All) {
212 		validatebool("TransparentBg", &cfg.transbg, TRANSBG);
213 		validatebool("LargeFonts", &cfg.largefonts, LARGEFONTS);
214 		validateint("LineSpacingAdjustment", &cfg.linespaceadjust, LINESPACEADJUST, -5, 10);
215 		validateint("ImageScale", &cfg.imagescale, IMAGESCALE, 50, 500);
216 		validateint("5MinuteGraphResultCount", &cfg.fivegresultcount, FIVEGRESULTCOUNT, FIVEGMINRESULTCOUNT, 2000);
217 		validateint("5MinuteGraphHeight", &cfg.fivegheight, FIVEGHEIGHT, FIVEGMINHEIGHT, 2000);
218 		validatebool("SummaryGraph", &cfg.summarygraph, SUMMARYGRAPH);
219 		validateint("EstimateStyle", &cfg.estimatestyle, ESTIMATESTYLE, 0, 2);
220 		validatebool("BarColumnShowsRate", &cfg.barshowsrate, BARSHOWSRATE);
221 		validatebool("HourlyRate", &cfg.hourlyrate, HOURLYRATE);
222 		validatebool("SummaryRate", &cfg.summaryrate, SUMMARYRATE);
223 	}
224 	validatebool("TrafficlessEntries", &cfg.trafficlessentries, TRAFFICLESSENTRIES);
225 	validateint("List5Mins", &cfg.listfivemins, LISTFIVEMINS, 0, 0);
226 	validateint("ListHours", &cfg.listhours, LISTHOURS, 0, 0);
227 	validateint("ListDays", &cfg.listdays, LISTDAYS, 0, 0);
228 	validateint("ListMonths", &cfg.listmonths, LISTMONTHS, 0, 0);
229 	validateint("ListYears", &cfg.listyears, LISTYEARS, 0, 0);
230 	validateint("ListTop", &cfg.listtop, LISTTOP, 0, 0);
231 	validateint("5MinuteHours", &cfg.fiveminutehours, FIVEMINUTEHOURS, -1, -1);
232 	validateint("HourlyDays", &cfg.hourlydays, HOURLYDAYS, -1, -1);
233 	validateint("DailyDays", &cfg.dailydays, DAILYDAYS, -1, -1);
234 	validateint("MonthlyMonths", &cfg.monthlymonths, MONTHLYMONTHS, -1, -1);
235 	validateint("YearlyYears", &cfg.yearlyyears, YEARLYYEARS, -1, -1);
236 	validateint("TopDayEntries", &cfg.topdayentries, TOPDAYENTRIES, -1, -1);
237 	validatebool("BandwidthDetection", &cfg.bwdetection, BWDETECT);
238 	validateint("BandwidthDetectionInterval", &cfg.bwdetectioninterval, BWDETECTINTERVAL, 0, 30);
239 	validatebool("Experimental", &cfg.experimental, 0);
240 
241 	if (cfg.useutc) {
242 		strncpy_nt(cfg.dbtzmodifier, "", 14);
243 	} else {
244 		strncpy_nt(cfg.dbtzmodifier, DATABASELOCALTIMEMODIFIER, 14);
245 	}
246 
247 	if (cfg.dbdir[0] != '/') {
248 		strncpy_nt(cfg.dbdir, DATABASEDIR, 512);
249 		snprintf(errorstring, 1024, "DatabaseDir %s", noslashstart);
250 		printe(PT_Config);
251 	}
252 
253 	if (cfg.logfile[0] != '/') {
254 		strncpy_nt(cfg.logfile, LOGFILE, 512);
255 		snprintf(errorstring, 1024, "LogFile %s", noslashstart);
256 		printe(PT_Config);
257 	}
258 
259 	if (cfg.pidfile[0] != '/') {
260 		strncpy_nt(cfg.pidfile, PIDFILE, 512);
261 		snprintf(errorstring, 1024, "PidFile %s", noslashstart);
262 		printe(PT_Config);
263 	}
264 
265 	if (type == CT_Daemon || type == CT_All) {
266 		validatebool("RescanDatabaseOnSave", &cfg.rescanonsave, RESCANONSAVE);
267 		validatebool("AlwaysAddNewInterfaces", &cfg.alwaysadd, ALWAYSADD);
268 
269 		if (cfg.updateinterval < cfg.pollinterval || cfg.updateinterval > 300) {
270 			if (cfg.pollinterval > UPDATEINTERVAL) {
271 				cfg.updateinterval = cfg.pollinterval;
272 			} else {
273 				cfg.updateinterval = UPDATEINTERVAL;
274 			}
275 			snprintf(errorstring, 1024, "%s UpdateInterval, %s %d.", invalidvalue, resettingto, cfg.updateinterval);
276 			printe(PT_Config);
277 		}
278 
279 		if ((cfg.saveinterval * 60) < cfg.updateinterval || cfg.saveinterval > 60) {
280 			if (cfg.updateinterval > (SAVEINTERVAL * 60)) {
281 				cfg.saveinterval = cfg.updateinterval;
282 			} else {
283 				cfg.saveinterval = SAVEINTERVAL;
284 			}
285 			snprintf(errorstring, 1024, "%s SaveInterval, %s %d.", invalidvalue, resettingto, cfg.saveinterval);
286 			printe(PT_Config);
287 		}
288 
289 		if (cfg.offsaveinterval < cfg.saveinterval || cfg.offsaveinterval > 60) {
290 			if (cfg.saveinterval > OFFSAVEINTERVAL) {
291 				cfg.offsaveinterval = cfg.saveinterval;
292 			} else {
293 				cfg.offsaveinterval = OFFSAVEINTERVAL;
294 			}
295 			snprintf(errorstring, 1024, "%s OfflineSaveInterval, %s %d.", invalidvalue, resettingto, cfg.offsaveinterval);
296 			printe(PT_Config);
297 		}
298 
299 		/* enforce update interval to be short enough that 32-bit interface counter rollover can be detected */
300 		/* 1.02 is the same 2% safety buffer as used in processifinfo() in daemon.c */
301 		if (cfg.maxbw > 0) {
302 			rolloversecs = (uint32_t)((float)MAX32 / ((float)cfg.maxbw * 1024 * 1024 * (float)1.02 / 8));
303 			if (rolloversecs <= (uint32_t)cfg.updateinterval) {
304 				cfg.updateinterval = UPDATEINTERVAL;
305 				if (rolloversecs <= (uint32_t)cfg.updateinterval) {
306 					cfg.updateinterval /= 2;
307 				}
308 				snprintf(errorstring, 1024, "UpdateInterval has been reset to %d seconds in order to ensure correct counter rollover detection at %d Mbit.", cfg.updateinterval, cfg.maxbw);
309 				printe(PT_Config);
310 			}
311 		}
312 	}
313 
314 	/* affects only image output */
315 	if (type == CT_Image || type == CT_All) {
316 		if (cfg.barshowsrate && cfg.estimatebarvisible) {
317 			cfg.estimatestyle = 0;
318 			if (debug) {
319 				printf("BarColumnShowsRate and EstimateBarVisible both enabled -> EstimateStyle set to 0\n");
320 			}
321 		}
322 
323 		if (cfg.fiveminutehours > 0 && cfg.fivegresultcount > cfg.fiveminutehours * 12) {
324 			if (cfg.fiveminutehours * 12 < FIVEGMINRESULTCOUNT) {
325 				snprintf(errorstring, 1024, "Value \"%d\" for 5MinuteHours is too small for 5MinuteGraphResultCount with value \"%d\" (smallest supported value: %d). Value for 5MinuteHours needs to be at least %d if 5MinuteGraphResultCount is set to %d.", cfg.fiveminutehours, cfg.fivegresultcount, FIVEGMINRESULTCOUNT, FIVEGMINRESULTCOUNT / 12, FIVEGMINRESULTCOUNT);
326 				cfg.fivegresultcount = FIVEGMINRESULTCOUNT;
327 			} else {
328 				snprintf(errorstring, 1024, "5MinuteGraphResultCount has been adjusted to %d because requested \"%d\" requires more data than can be available with value %d for 5MinuteHours.", cfg.fiveminutehours * 12, cfg.fivegresultcount, cfg.fiveminutehours);
329 				cfg.fivegresultcount = cfg.fiveminutehours * 12;
330 			}
331 			printe(PT_Config);
332 		}
333 	}
334 }
335 
defaultcfg(void)336 void defaultcfg(void)
337 {
338 	ifacebw = NULL;
339 
340 	cfg.bvar = BVAR;
341 	cfg.qmode = DEFQMODE;
342 	cfg.sampletime = DEFSAMPTIME;
343 	cfg.monthrotate = MONTHROTATE;
344 	cfg.monthrotateyears = MONTHROTATEYEARS;
345 	cfg.unitmode = UNITMODE;
346 	cfg.rateunitmode = RATEUNITMODE;
347 	cfg.ostyle = OSTYLE;
348 	cfg.estimatebarvisible = ESTIMATEBARVISIBLE;
349 	cfg.rateunit = RATEUNIT;
350 	cfg.defaultdecimals = DEFAULTDECIMALS;
351 	cfg.hourlydecimals = HOURLYDECIMALS;
352 	cfg.hourlystyle = HOURLYSTYLE;
353 	cfg.bwdetection = BWDETECT;
354 	cfg.bwdetectioninterval = BWDETECTINTERVAL;
355 	cfg.maxbw = DEFMAXBW;
356 	cfg.spacecheck = USESPACECHECK;
357 	cfg.hourlyrate = HOURLYRATE;
358 	cfg.summaryrate = SUMMARYRATE;
359 	cfg.trafficlessentries = TRAFFICLESSENTRIES;
360 	cfg.utflocale = UTFLOCALE;
361 
362 	cfg.listfivemins = LISTFIVEMINS;
363 	cfg.listhours = LISTHOURS;
364 	cfg.listdays = LISTDAYS;
365 	cfg.listmonths = LISTMONTHS;
366 	cfg.listyears = LISTYEARS;
367 	cfg.listtop = LISTTOP;
368 	cfg.listjsonxml = LISTJSONXML;
369 
370 	cfg.fiveminutehours = FIVEMINUTEHOURS;
371 	cfg.hourlydays = HOURLYDAYS;
372 	cfg.dailydays = DAILYDAYS;
373 	cfg.monthlymonths = MONTHLYMONTHS;
374 	cfg.yearlyyears = YEARLYYEARS;
375 	cfg.topdayentries = TOPDAYENTRIES;
376 
377 	strncpy_nt(cfg.dbdir, DATABASEDIR, 512);
378 	strncpy_nt(cfg.dbtzmodifier, DATABASELOCALTIMEMODIFIER, 14);
379 	strncpy_nt(cfg.iface, DEFIFACE, 32);
380 	strncpy_nt(cfg.locale, LOCALE, 32);
381 	strncpy_nt(cfg.dformat, DFORMAT, 64);
382 	strncpy_nt(cfg.mformat, MFORMAT, 64);
383 	strncpy_nt(cfg.tformat, TFORMAT, 64);
384 	strncpy_nt(cfg.hformat, HFORMAT, 64);
385 	strncpy_nt(cfg.rxchar, RXCHAR, 2);
386 	strncpy_nt(cfg.txchar, TXCHAR, 2);
387 	strncpy_nt(cfg.rxhourchar, RXHOURCHAR, 2);
388 	strncpy_nt(cfg.txhourchar, TXHOURCHAR, 2);
389 
390 	cfg.daemonuser[0] = '\0';
391 	cfg.daemongroup[0] = '\0';
392 	cfg.timesyncwait = TIMESYNCWAIT;
393 	cfg.updateinterval = UPDATEINTERVAL;
394 	cfg.pollinterval = POLLINTERVAL;
395 	cfg.saveinterval = SAVEINTERVAL;
396 	cfg.offsaveinterval = OFFSAVEINTERVAL;
397 	cfg.rescanonsave = RESCANONSAVE;
398 	cfg.alwaysadd = ALWAYSADD;
399 	cfg.savestatus = SAVESTATUS;
400 	cfg.uselogging = USELOGGING;
401 	cfg.createdirs = CREATEDIRS;
402 	cfg.updatefileowner = UPDATEFILEOWNER;
403 	strncpy_nt(cfg.logfile, LOGFILE, 512);
404 	strncpy_nt(cfg.pidfile, PIDFILE, 512);
405 	cfg.is64bit = IS64BIT;
406 	cfg.waldb = WALDB;
407 	cfg.dbsynchronous = DBSYNCHRONOUS;
408 	cfg.useutc = USEUTC;
409 
410 	cfg.transbg = TRANSBG;
411 	cfg.largefonts = LARGEFONTS;
412 	cfg.linespaceadjust = LINESPACEADJUST;
413 	cfg.imagescale = IMAGESCALE;
414 	cfg.fivegresultcount = FIVEGRESULTCOUNT;
415 	cfg.fivegheight = FIVEGHEIGHT;
416 	cfg.summarygraph = SUMMARYGRAPH;
417 	cfg.estimatestyle = ESTIMATESTYLE;
418 	cfg.barshowsrate = BARSHOWSRATE;
419 	strncpy_nt(cfg.cbg, CBACKGROUND, 8);
420 	strncpy_nt(cfg.cedge, CEDGE, 8);
421 	strncpy_nt(cfg.cheader, CHEADER, 8);
422 	strncpy_nt(cfg.cheadertitle, CHEADERTITLE, 8);
423 	strncpy_nt(cfg.cheaderdate, CHEADERDATE, 8);
424 	strncpy_nt(cfg.ctext, CTEXT, 8);
425 	strncpy_nt(cfg.cline, CLINE, 8);
426 	strncpy_nt(cfg.clinel, CLINEL, 8);
427 	strncpy_nt(cfg.crx, CRX, 8);
428 	strncpy_nt(cfg.crxd, CRXD, 8);
429 	strncpy_nt(cfg.ctx, CTX, 8);
430 	strncpy_nt(cfg.ctxd, CTXD, 8);
431 
432 	cfg.experimental = 0;
433 }
434 
opencfgfile(const char * cfgfile,FILE ** fd)435 int opencfgfile(const char *cfgfile, FILE **fd)
436 {
437 	char buffer[512];
438 	int i, tryhome;
439 
440 	/* clear buffer */
441 	for (i = 0; i < 512; i++) {
442 		buffer[i] = '\0';
443 	}
444 	cfg.cfgfile[0] = '\0';
445 
446 	/* possible config files: 1) --config   2) $HOME/.vnstatrc   3) /etc/vnstat.conf   4) none */
447 
448 	if (cfgfile[0] != '\0') {
449 
450 		/* try to open given file */
451 		if ((*fd = fopen(cfgfile, "r")) != NULL) {
452 			strncpy_nt(cfg.cfgfile, cfgfile, 512);
453 			if (debug)
454 				printf("Config file: --config\n");
455 		} else {
456 			snprintf(errorstring, 1024, "Unable to open given config file \"%s\": %s", cfgfile, strerror(errno));
457 			printe(PT_Error);
458 			return 0;
459 		}
460 
461 	} else {
462 
463 		if (getenv("HOME")) {
464 			strncpy_nt(buffer, getenv("HOME"), 500);
465 			strcat(buffer, "/.vnstatrc");
466 			tryhome = 1;
467 		} else {
468 			tryhome = 0;
469 		}
470 
471 		/* try to open first available config file */
472 		if (tryhome && (*fd = fopen(buffer, "r")) != NULL) {
473 			strncpy_nt(cfg.cfgfile, buffer, 512);
474 		} else if ((*fd = fopen("/etc/vnstat.conf", "r")) != NULL) {
475 			snprintf(cfg.cfgfile, 512, "/etc/vnstat.conf");
476 		} else if ((*fd = fopen("/usr/local/etc/vnstat.conf", "r")) != NULL) {
477 			snprintf(cfg.cfgfile, 512, "/usr/local/etc/vnstat.conf");
478 		} else {
479 			if (debug)
480 				printf("Config file: none\n");
481 			return 1;
482 		}
483 		if (debug)
484 			printf("Config file: %s\n", cfg.cfgfile);
485 	}
486 
487 	return 2;
488 }
489 
extractcfgvalue(char * value,const unsigned int valuelen,const char * cfgline,const unsigned int cfglen)490 int extractcfgvalue(char *value, const unsigned int valuelen, const char *cfgline, const unsigned int cfglen)
491 {
492 
493 	unsigned int i, j, linelen;
494 
495 	linelen = (unsigned int)strlen(cfgline);
496 
497 	for (i = 0; i < valuelen; i++) {
498 		value[i] = '\0';
499 	}
500 
501 	i = 0;
502 	for (j = cfglen; j < linelen; j++) {
503 		if (cfgline[j] == '\n' || cfgline[j] == '\r') {
504 			break;
505 		} else if (cfgline[j] == '\"') {
506 			if (i == 0) {
507 				continue;
508 			} else {
509 				break;
510 			}
511 		} else {
512 			if (i == 0 && (cfgline[j] == ' ' || cfgline[j] == '=' || cfgline[j] == '\t')) {
513 				continue;
514 			} else {
515 				value[i] = cfgline[j];
516 				i++;
517 			}
518 		}
519 	}
520 
521 	return (int)strlen(value);
522 }
523 
setcfgvalue(const struct cfgsetting * cset,const char * value,const char * cfgline)524 int setcfgvalue(const struct cfgsetting *cset, const char *value, const char *cfgline)
525 {
526 	if (cset->namelen > 0) {
527 		strncpy_nt(cset->locc, value, (size_t)cset->namelen);
528 		if (debug)
529 			printf("  c: %s   -> \"%s\": \"%s\"\n", cfgline, cset->name, cset->locc);
530 	} else if ((strlen(value) > 1 && isdigit(value[1])) || isdigit(value[0])) {
531 		*cset->loci = (int32_t)strtol(value, (char **)NULL, 0);
532 		if (debug)
533 			printf("  i: %s   -> \"%s\": %d\n", cfgline, cset->name, *cset->loci);
534 	} else {
535 		return 0;
536 	}
537 
538 	return 1;
539 }
540 
configlocale(void)541 void configlocale(void)
542 {
543 	if (cfg.locale[0] != '-' && strlen(cfg.locale) > 0) {
544 		setlocale(LC_ALL, cfg.locale);
545 	} else {
546 		if (getenv("LC_ALL")) {
547 			setlocale(LC_ALL, getenv("LC_ALL"));
548 		} else {
549 			setlocale(LC_ALL, "");
550 		}
551 	}
552 	if (getenv("LC_ALL")) {
553 		if (strstr(getenv("LC_ALL"), "UTF") != NULL) {
554 			cfg.utflocale = 1;
555 		} else {
556 			cfg.utflocale = 0;
557 		}
558 	}
559 }
560