1 /*
2  * SNOOPY LOGGER
3  *
4  * File: configfile.c
5  *
6  * Copyright (c) 2014-2015 Bostjan Skufca <bostjan@a2o.si>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22 
23 
24 
25 /*
26  * Includes order: from local to global
27  */
28 #include "configfile.h"
29 
30 #include "snoopy.h"
31 #include "configfile.h"
32 #include "configuration.h"
33 #include "misc.h"
34 #include "outputregistry.h"
35 
36 #include "lib/inih/src/ini.h"
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 
44 
45 
46 /*
47  * snoopy_configfile_load_file
48  *
49  * Description:
50  *     Parses INI configuration file and overrides Snoopy
51  *     configuration with changed values.
52  *
53  * Params:
54  *     file   Path log INI configuration file
55  *
56  * Return:
57  *     int    0 on success, -1 on error openinf file, other int for other errors
58  */
snoopy_configfile_load(char * iniFilePath)59 int snoopy_configfile_load (
60     char *iniFilePath
61 ) {
62     int         iniParseStatus;
63     snoopy_configuration_t *CFG;
64 
65 
66     /* Get config pointer */
67     CFG = snoopy_configuration_get();
68 
69 
70     /* Tell Snoopy we are using configuration file */
71     CFG->configfile_path = iniFilePath;
72 
73     /* Parse the INI configuration file first */
74     iniParseStatus = ini_parse(iniFilePath, snoopy_configfile_parser_callback, CFG);
75     if (0 != iniParseStatus) {
76         return -1;
77     }
78     CFG->configfile_found = SNOOPY_TRUE;
79 
80 
81     /* Housekeeping */
82     CFG->configfile_parsed = SNOOPY_TRUE;   // We have successfully parsed configuration file
83     return 0;
84 }
85 
86 
87 
88 
89 /*
90  * snoopy_configfile_parser_callback
91  *
92  * Description:
93  *     Callback function for each found ini value in parsed config file.
94  *
95  * Params:
96  *     ...
97  *
98  * Return:
99  *     ...
100  */
snoopy_configfile_parser_callback(void * sth,const char * section,const char * name,const char * confValString)101 int snoopy_configfile_parser_callback (
102     void* sth,
103     const char* section,
104     const char* name,
105     const char* confValString
106 ) {
107     int confValInt;
108 
109 
110     /* Qualify pointer? */
111     snoopy_configuration_t* CFG = (snoopy_configuration_t*)sth;
112 
113 
114     /* Skip unknown sections */
115     if (0 != strcmp(section, "snoopy")) {
116         return 1;
117     }
118 
119 
120 
121     /* Do the name matching */
122     #define MATCHNAME(n) strcmp(name, n) == 0
123 
124 
125     if (MATCHNAME("error_logging")) {
126         confValInt = snoopy_configfile_getboolean(confValString, -1);
127         if (-1 != confValInt) {
128             CFG->error_logging_enabled = confValInt;
129         }
130         return 1;
131     }
132 
133 
134     if (MATCHNAME("message_format")) {
135         CFG->message_format          = strdup(confValString);
136         CFG->message_format_malloced = SNOOPY_TRUE;
137         return 1;
138     }
139 
140 
141     if (MATCHNAME("filter_chain")) {
142         CFG->filter_chain          = strdup(confValString);
143         CFG->filter_chain_malloced = SNOOPY_TRUE;
144         return 1;
145     }
146 
147 
148     if (MATCHNAME("output")) {
149         snoopy_configfile_parse_output(confValString);
150         return 1;
151     }
152 
153 
154     if (MATCHNAME("syslog_facility")) {
155         snoopy_configfile_parse_syslog_facility(confValString);
156         return 1;
157     }
158 
159 
160     if (MATCHNAME("syslog_ident")) {
161         CFG->syslog_ident          = strdup(confValString);
162         CFG->syslog_ident_malloced = SNOOPY_TRUE;
163         return 1;
164     }
165 
166 
167     if (MATCHNAME("syslog_level")) {
168         snoopy_configfile_parse_syslog_level(confValString);
169         return 1;
170     }
171 
172 
173     /* Why are we returning 1 instead of zero everywhere? */
174     return 1;
175 }
176 
177 
178 
179 /*
180  * snoopy_configfile_parse_output
181  *
182  * Description:
183  *     Parses configuration setting syslog_output and
184  *     sets appropriate internal configuration variable(s).
185  *     Uses default setting if unknown value.
186  *
187  * Params:
188  *     confVal   Value from configuration file
189  *
190  * Return:
191  *     void
192  */
snoopy_configfile_parse_output(const char * confValOrig)193 void snoopy_configfile_parse_output (
194     const char *confValOrig
195 ) {
196     char  *confVal;
197     const char * outputName;
198     const char * outputArg;
199     int    outputArgFound = SNOOPY_FALSE;
200     snoopy_configuration_t *CFG;
201 
202 
203     /* Get config pointer */
204     CFG = snoopy_configuration_get();
205 
206 
207     // Do not assign null to it explicitly, as you get "Explicit null dereference" Coverity error.
208     // If you do not assign it, Coverity complains with "Uninitialized pointer read".
209     char  *saveptr1 = "";
210 
211     // First clone the config value, as it gets freed by ini parsing library
212     confVal = strdup(confValOrig);
213 
214     // Check if configured value contains argument(s)
215     if (NULL == strchr(confVal, ':')) {
216         outputName = confVal;
217         CFG->output_arg          = "";
218         CFG->output_arg_malloced = SNOOPY_FALSE;
219         outputArg  = "";
220     } else {
221         // Separate output name from its arguments
222         outputName = strtok_r(confVal, ":", &saveptr1);
223         outputArg  = strtok_r(NULL   , ":", &saveptr1);
224         outputArgFound = SNOOPY_TRUE;
225 
226     }
227 
228     // Determine output name
229     if (SNOOPY_TRUE == snoopy_outputregistry_doesNameExist(outputName)) {
230         CFG->output          = strdup(outputName);
231         CFG->output_malloced = SNOOPY_TRUE;
232 
233         if (SNOOPY_TRUE == outputArgFound) {
234             // THINK What if conf.output_arg was set in previous call to this function,
235             // and is already malloced? We need to detect that and free previous
236             // allocation.
237             CFG->output_arg          = strdup(outputArg);
238             CFG->output_arg_malloced = SNOOPY_TRUE;
239         }
240     } else {
241         CFG->output              = SNOOPY_OUTPUT_DEFAULT;
242         CFG->output_malloced     = SNOOPY_FALSE;
243         CFG->output_arg          = SNOOPY_OUTPUT_DEFAULT_ARG;
244         CFG->output_arg_malloced = SNOOPY_FALSE;
245     }
246 
247     // Housekeeping
248     free(confVal);
249 }
250 
251 
252 
253 /*
254  * snoopy_configfile_parse_syslog_facility
255  *
256  * Description:
257  *     Parses configuration setting syslog_facility and
258  *     sets appropriate config variable.
259  *     Uses default setting if unknown value.
260  *
261  * Params:
262  *     confVal   Value from configuration file
263  *
264  * Return:
265  *     void
266  */
snoopy_configfile_parse_syslog_facility(const char * confValOrig)267 void snoopy_configfile_parse_syslog_facility (
268     const char *confValOrig
269 ) {
270     char *confVal;
271     const char *confValCleaned;
272     int   facilityInt;
273     snoopy_configuration_t *CFG;
274 
275 
276     /* Get config pointer */
277     CFG = snoopy_configuration_get();
278 
279     // Duplicate the ini value, as we need to modify it
280     confVal = strdup(confValOrig);
281 
282     // First cleanup the value
283     confValCleaned = snoopy_configfile_syslog_value_cleanup(confVal);
284 
285     // Evaluate and set configuration flag
286     facilityInt = snoopy_syslog_convert_facilityToInt(confValCleaned);
287     if (-1 == facilityInt) {
288         CFG->syslog_facility = SNOOPY_SYSLOG_FACILITY;
289     } else {
290         CFG->syslog_facility = facilityInt;
291     }
292 
293     /* Housekeeping */
294     free(confVal);
295 }
296 
297 
298 
299 /*
300  * snoopy_configfile_parse_syslog_level
301  *
302  * Description:
303  *     Parses configuration setting syslog_level and
304  *     sets appropriate config variable.
305  *     Uses default setting if unknown value.
306  *
307  * Params:
308  *     confVal   Value from configuration file
309  *
310  * Return:
311  *     void
312  */
snoopy_configfile_parse_syslog_level(const char * confValOrig)313 void snoopy_configfile_parse_syslog_level (
314     const char *confValOrig
315 ) {
316     char *confVal;
317     const char *confValCleaned;
318     int   levelInt;
319     snoopy_configuration_t *CFG;
320 
321 
322     /* Get config pointer */
323     CFG = snoopy_configuration_get();
324 
325     // Duplicate the ini value, as we need to modify it
326     confVal = strdup(confValOrig);
327 
328     // First cleanup the value
329     confValCleaned = snoopy_configfile_syslog_value_cleanup(confVal);
330 
331     // Evaluate and set configuration flag
332     levelInt = snoopy_syslog_convert_levelToInt(confValCleaned);
333     if (-1 == levelInt) {
334         CFG->syslog_level = SNOOPY_SYSLOG_LEVEL;
335     } else {
336         CFG->syslog_level = levelInt;
337     }
338 
339     /* Housekeeping */
340     free(confVal);
341 }
342 
343 
344 
345 /*
346  * snoopy_configfile_syslog_value_cleanup
347  *
348  * Description:
349  *     Convert existing string to upper case, and remove LOG_ prefix
350  *
351  * Params:
352  *     confVal   Pointer to string to change and to be operated on
353  *
354  * Return:
355  *     char *    Pointer to cleaned string (either the same as initial argument,
356  *               or 4 characters advanced, to remove LOG_ prefix
357  */
snoopy_configfile_syslog_value_cleanup(char * confVal)358 char *snoopy_configfile_syslog_value_cleanup (char *confVal)
359 {
360     char *confValCleaned;
361 
362     // Convert to upper case
363     snoopy_configfile_strtoupper(confVal);
364 
365     // Remove LOG_ prefix
366     confValCleaned = snoopy_configfile_syslog_value_remove_prefix(confVal);
367 
368     return confValCleaned;
369 }
370 
371 
372 
373 /*
374  * snoopy_configfile_syslog_value_remove_prefix
375  *
376  * Description:
377  *     Remove the LOG_ prefix, return pointer to new string (either equal
378  *     or +4 chars advanced)
379  *
380  * Params:
381  *     string   Pointer to string to remove LOG_ prefix
382  *
383  * Return:
384  *     char *   Pointer to non LOG_ part of the string
385  */
snoopy_configfile_syslog_value_remove_prefix(char * confVal)386 char *snoopy_configfile_syslog_value_remove_prefix (char *confVal)
387 {
388     if (0 == strncmp(confVal, "LOG_", 4)) {
389         return confVal+4;
390     } else {
391         return confVal;
392     }
393 }
394 
395 
396 
397 /*
398  * snoopy_configfile_strtoupper
399  *
400  * Description:
401  *     Convert existing string to upper case
402  *
403  * Params:
404  *     string   Pointer to string to change and to be operated on
405  *
406  * Return:
407  *     void
408  */
snoopy_configfile_strtoupper(char * s)409 void snoopy_configfile_strtoupper (char *s)
410 {
411     while (*s) {
412         if ((*s >= 'a' ) && (*s <= 'z')) {
413             *s -= ('a'-'A');
414         }
415         s++;
416     }
417 }
418 
419 
420 
421 /*-------------------------------------------------------------------------*/
422 /**
423   @origin   Literally copy-pasted from ndevilla's iniparser
424 
425 
426   @brief    Get the string associated to a key, convert to a boolean
427   @param    d Dictionary to search
428   @param    key Key string to look for
429   @param    notfound Value to return in case of error
430   @return   integer
431 
432   This function queries a dictionary for a key. A key as read from an
433   ini file is given as "section:key". If the key cannot be found,
434   the notfound value is returned.
435 
436   A true boolean is found if one of the following is matched:
437 
438   - A string starting with 'y'
439   - A string starting with 'Y'
440   - A string starting with 't'
441   - A string starting with 'T'
442   - A string starting with '1'
443 
444   A false boolean is found if one of the following is matched:
445 
446   - A string starting with 'n'
447   - A string starting with 'N'
448   - A string starting with 'f'
449   - A string starting with 'F'
450   - A string starting with '0'
451 
452   The notfound value returned if no boolean is identified, does not
453   necessarily have to be 0 or 1.
454  */
455 /*--------------------------------------------------------------------------*/
snoopy_configfile_getboolean(const char * c,int notfound)456 int snoopy_configfile_getboolean (const char *c, int notfound)
457 {
458     int   ret;
459 
460     if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
461         ret = 1 ;
462     } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
463         ret = 0 ;
464     } else {
465         ret = notfound ;
466     }
467     return ret;
468 }
469