1 /* cfsysline.c
2  * Implementation of the configuration system line object.
3  *
4  * File begun on 2007-07-30 by RGerhards
5  *
6  * Copyright (C) 2007-2020 Adiscon GmbH.
7  *
8  * This file is part of rsyslog.
9  *
10  * This file is part of the rsyslog runtime library.
11  *
12  * Licensed under the Apache License, Version 2.0 (the "License");
13  * you may not use this file except in compliance with the License.
14  * You may obtain a copy of the License at
15  *
16  *       http://www.apache.org/licenses/LICENSE-2.0
17  *       -or-
18  *       see COPYING.ASL20 in the source distribution
19  *
20  * Unless required by applicable law or agreed to in writing, software
21  * distributed under the License is distributed on an "AS IS" BASIS,
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23  * See the License for the specific language governing permissions and
24  * limitations under the License.
25  */
26 #include "config.h"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <pwd.h>
35 #include <grp.h>
36 
37 #include "rsyslog.h"
38 #include "cfsysline.h"
39 #include "obj.h"
40 #include "conf.h"
41 #include "errmsg.h"
42 #include "srUtils.h"
43 #include "unicode-helper.h"
44 #include "rsconf.h"
45 #include "parserif.h"
46 
47 
48 /* static data */
DEFobjCurrIf(obj)49 DEFobjCurrIf(obj)
50 
51 linkedList_t llCmdList; /* this is NOT a pointer - no typo here ;) */
52 
53 /* --------------- START functions for handling canned syntaxes --------------- */
54 
55 
56 /* parse a character from the config line
57  * added 2007-07-17 by rgerhards
58  * TODO: enhance this function to handle different classes of characters
59  * HINT: check if char is ' and, if so, use 'c' where c may also be things
60  * like \t etc.
61  */
62 static rsRetVal doGetChar(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
63 {
64 	DEFiRet;
65 
66 	assert(pp != NULL);
67 	assert(*pp != NULL);
68 
69 	skipWhiteSpace(pp); /* skip over any whitespace */
70 
71 	/* if we are not at a '\0', we have our new char - no validity checks here... */
72 	if(**pp == '\0') {
73 		LogError(0, RS_RET_NOT_FOUND, "No character available");
74 		iRet = RS_RET_NOT_FOUND;
75 	} else {
76 		if(pSetHdlr == NULL) {
77 			/* we should set value directly to var */
78 			*((uchar*)pVal) = **pp;
79 		} else {
80 			/* we set value via a set function */
81 			CHKiRet(pSetHdlr(pVal, **pp));
82 		}
83 		++(*pp); /* eat processed char */
84 	}
85 
86 finalize_it:
87 	RETiRet;
88 }
89 
90 
91 /* Parse a number from the configuration line. This is more or less
92  * a shell to call the custom handler.
93  * rgerhards, 2007-07-31
94  */
doCustomHdlr(uchar ** pp,rsRetVal (* pSetHdlr)(uchar **,void *),void * pVal)95 static rsRetVal doCustomHdlr(uchar **pp, rsRetVal (*pSetHdlr)(uchar**, void*), void *pVal)
96 {
97 	DEFiRet;
98 
99 	assert(pp != NULL);
100 	assert(*pp != NULL);
101 
102 	CHKiRet(pSetHdlr(pp, pVal));
103 
104 finalize_it:
105 	RETiRet;
106 }
107 
108 
109 /* Parse a number from the configuration line. This functions just parses
110  * the number and does NOT call any handlers or set any values. It is just
111  * for INTERNAL USE by other parse functions!
112  * rgerhards, 2008-01-08
113  */
parseIntVal(uchar ** pp,int64 * pVal)114 static rsRetVal parseIntVal(uchar **pp, int64 *pVal)
115 {
116 	DEFiRet;
117 	uchar *p;
118 	int64 i;
119 	int bWasNegative;
120 
121 	assert(pp != NULL);
122 	assert(*pp != NULL);
123 	assert(pVal != NULL);
124 
125 	skipWhiteSpace(pp); /* skip over any whitespace */
126 	p = *pp;
127 
128 	if(*p == '-') {
129 		bWasNegative = 1;
130 		++p; /* eat it */
131 	} else {
132 		bWasNegative = 0;
133 	}
134 
135 	if(!isdigit((int) *p)) {
136 		errno = 0;
137 		LogError(0, RS_RET_INVALID_INT, "invalid number");
138 		ABORT_FINALIZE(RS_RET_INVALID_INT);
139 	}
140 
141 	/* pull value */
142 	for(i = 0 ; *p && (isdigit((int) *p) || *p == '.' || *p == ',')  ; ++p) {
143 		if(isdigit((int) *p)) {
144 			i = i * 10 + *p - '0';
145 		}
146 	}
147 
148 	if(bWasNegative)
149 		i *= -1;
150 
151 	*pVal = i;
152 	*pp = p;
153 
154 finalize_it:
155 	RETiRet;
156 }
157 
158 
159 /* Parse a size from the configuration line. This is basically an integer
160  * syntax, but modifiers may be added after the integer (e.g. 1k to mean
161  * 1024). The size must immediately follow the number. Note that the
162  * param value must be int64!
163  * rgerhards, 2008-01-09
164  */
doGetSize(uchar ** pp,rsRetVal (* pSetHdlr)(void *,int64),void * pVal)165 static rsRetVal doGetSize(uchar **pp, rsRetVal (*pSetHdlr)(void*, int64), void *pVal)
166 {
167 	DEFiRet;
168 	int64 i;
169 
170 	assert(pp != NULL);
171 	assert(*pp != NULL);
172 
173 	CHKiRet(parseIntVal(pp, &i));
174 
175 	/* we now check if the next character is one of our known modifiers.
176 	 * If so, we accept it as such. If not, we leave it alone. tera and
177 	 * above does not make any sense as that is above a 32-bit int value.
178 	 */
179 	switch(**pp) {
180 		/* traditional binary-based definitions */
181 		case 'k': i *= 1024; ++(*pp); break;
182 		case 'm': i *= 1024 * 1024; ++(*pp); break;
183 		case 'g': i *= 1024 * 1024 * 1024; ++(*pp); break;
184 		case 't': i *= (int64) 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* tera */
185 		case 'p': i *= (int64) 1024 * 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* peta */
186 		case 'e': i *= (int64) 1024 * 1024 * 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* exa */
187 		/* and now the "new" 1000-based definitions */
188 		case 'K': i *= 1000; ++(*pp); break;
189 		case 'M': i *= 1000000; ++(*pp); break;
190 		case 'G': i *= 1000000000; ++(*pp); break;
191 			  /* we need to use the multiplication below because otherwise
192 			   * the compiler gets an error during constant parsing */
193 		case 'T': i *= (int64) 1000       * 1000000000; ++(*pp); break; /* tera */
194 		case 'P': i *= (int64) 1000000    * 1000000000; ++(*pp); break; /* peta */
195 		case 'E': i *= (int64) 1000000000 * 1000000000; ++(*pp); break; /* exa */
196 	}
197 
198 	/* done */
199 	if(pSetHdlr == NULL) {
200 		/* we should set value directly to var */
201 		*((int64*)pVal) = i;
202 	} else {
203 		/* we set value via a set function */
204 		CHKiRet(pSetHdlr(pVal, i));
205 	}
206 
207 finalize_it:
208 	RETiRet;
209 }
210 
211 
212 /* Parse a number from the configuration line.
213  * rgerhards, 2007-07-31
214  */
doGetInt(uchar ** pp,rsRetVal (* pSetHdlr)(void *,uid_t),void * pVal)215 static rsRetVal doGetInt(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
216 {
217 	uchar *p;
218 	DEFiRet;
219 	int64 i;
220 
221 	assert(pp != NULL);
222 	assert(*pp != NULL);
223 
224 	CHKiRet(doGetSize(pp, NULL,&i));
225 	p = *pp;
226 	if(i > 2147483648ll) { /*2^31*/
227 		LogError(0, RS_RET_INVALID_VALUE,
228 		         "value %lld too large for integer argument.", i);
229 		ABORT_FINALIZE(RS_RET_INVALID_VALUE);
230 	}
231 
232 	if(pSetHdlr == NULL) {
233 		/* we should set value directly to var */
234 		*((int*)pVal) = (int) i;
235 	} else {
236 		/* we set value via a set function */
237 		CHKiRet(pSetHdlr(pVal, (int) i));
238 	}
239 
240 	*pp = p;
241 
242 finalize_it:
243 	RETiRet;
244 }
245 
246 
247 /* Parse and interpret a $FileCreateMode and $umask line. This function
248  * pulls the creation mode and, if successful, stores it
249  * into the global variable so that the rest of rsyslogd
250  * opens files with that mode. Any previous value will be
251  * overwritten.
252  * HINT: if we store the creation mode in selector_t, we
253  * can even specify multiple modes simply be virtue of
254  * being placed in the right section of rsyslog.conf
255  * rgerhards, 2007-07-4 (happy independence day to my US friends!)
256  * Parameter **pp has a pointer to the current config line.
257  * On exit, it will be updated to the processed position.
258  */
doFileCreateMode(uchar ** pp,rsRetVal (* pSetHdlr)(void *,uid_t),void * pVal)259 static rsRetVal doFileCreateMode(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
260 {
261 	uchar *p;
262 	DEFiRet;
263 	int iVal;
264 
265 	assert(pp != NULL);
266 	assert(*pp != NULL);
267 
268 	skipWhiteSpace(pp); /* skip over any whitespace */
269 	p = *pp;
270 
271 	/* for now, we parse and accept only octal numbers
272 	 * Sequence of tests is important, we are using boolean shortcuts
273 	 * to avoid addressing invalid memory!
274 	 */
275 	if(!(   (*p == '0')
276 	     && (*(p+1) && *(p+1) >= '0' && *(p+1) <= '7')
277 	     && (*(p+2) && *(p+2) >= '0' && *(p+2) <= '7')
278 	     && (*(p+3) && *(p+3) >= '0' && *(p+3) <= '7')  )  ) {
279 		LogError(0, RS_RET_INVALID_VALUE, "value must be octal (e.g 0644).");
280 		ABORT_FINALIZE(RS_RET_INVALID_VALUE);
281 	}
282 
283 	/*  we reach this code only if the octal number is ok - so we can now
284 	 *  compute the value.
285 	 */
286 	iVal  = (*(p+1)-'0') * 64 + (*(p+2)-'0') * 8 + (*(p+3)-'0');
287 
288 	if(pSetHdlr == NULL) {
289 		/* we should set value directly to var */
290 		*((int*)pVal) = iVal;
291 	} else {
292 		/* we set value via a set function */
293 		CHKiRet(pSetHdlr(pVal, iVal));
294 	}
295 
296 	p += 4;	/* eat the octal number */
297 	*pp = p;
298 
299 finalize_it:
300 	RETiRet;
301 }
302 
303 
304 /* Parse and interpret an on/off inside a config file line. This is most
305  * often used for boolean options, but of course it may also be used
306  * for other things. The passed-in pointer is updated to point to
307  * the first unparsed character on exit. Function emits error messages
308  * if the value is neither on or off. It returns 0 if the option is off,
309  * 1 if it is on and another value if there was an error.
310  * rgerhards, 2007-07-15
311  */
doParseOnOffOption(uchar ** pp)312 static int doParseOnOffOption(uchar **pp)
313 {
314 	uchar *pOptStart;
315 	uchar szOpt[32];
316 
317 	assert(pp != NULL);
318 	assert(*pp != NULL);
319 
320 	pOptStart = *pp;
321 	skipWhiteSpace(pp); /* skip over any whitespace */
322 
323 	if(getSubString(pp, (char*) szOpt, sizeof(szOpt), ' ')  != 0) {
324 		LogError(0, NO_ERRCODE, "Invalid $-configline - could not extract on/off option");
325 		return -1;
326 	}
327 
328 	if(!strcmp((char*)szOpt, "on")) {
329 		return 1;
330 	} else if(!strcmp((char*)szOpt, "off")) {
331 		return 0;
332 	} else {
333 		LogError(0, NO_ERRCODE, "Option value must be on or off, but is '%s'", (char*)pOptStart);
334 		return -1;
335 	}
336 }
337 
338 
339 /* extract a groupname and return its gid.
340  * rgerhards, 2007-07-17
341  */
doGetGID(uchar ** pp,rsRetVal (* pSetHdlr)(void *,uid_t),void * pVal)342 static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
343 {
344 	struct group *pgBuf = NULL;
345 	struct group gBuf;
346 	DEFiRet;
347 	uchar szName[256];
348 	int bufSize = 1024;
349 	char * stringBuf = NULL;
350 	int err;
351 
352 	assert(pp != NULL);
353 	assert(*pp != NULL);
354 
355 	if(getSubString(pp, (char*) szName, sizeof(szName), ' ')  != 0) {
356 		if(loadConf->globals.abortOnIDResolutionFail) {
357 			fprintf(stderr, "could not extract group name: %s\n", (char*)szName);
358 			exit(1); /* good exit */
359 		} else {
360 			LogError(0, RS_RET_NOT_FOUND, "could not extract group name");
361 			ABORT_FINALIZE(RS_RET_NOT_FOUND);
362 		}
363 	}
364 
365 	do {
366 		char *p;
367 
368 		/* Increase bufsize and try again.*/
369 		bufSize *= 2;
370 		CHKmalloc(p = realloc(stringBuf, bufSize));
371 		stringBuf = p;
372 		err = getgrnam_r((char*)szName, &gBuf, stringBuf, bufSize, &pgBuf);
373 	} while((pgBuf == NULL) && (err == ERANGE));
374 
375 	if(pgBuf == NULL) {
376 		if (err != 0) {
377 			LogError(err, RS_RET_NOT_FOUND, "Query for group '%s' resulted in an error",
378 				szName);
379 		} else {
380 			LogError(0, RS_RET_NOT_FOUND, "ID for group '%s' could not be found", szName);
381 		}
382 		iRet = RS_RET_NOT_FOUND;
383 		if(loadConf->globals.abortOnIDResolutionFail) {
384 			fprintf(stderr, "ID for group '%s' could not be found or error\n", szName);
385 			exit(1); /* good exit */
386 		}
387 	} else {
388 		if(pSetHdlr == NULL) {
389 			/* we should set value directly to var */
390 			*((gid_t*)pVal) = pgBuf->gr_gid;
391 		} else {
392 			/* we set value via a set function */
393 			CHKiRet(pSetHdlr(pVal, pgBuf->gr_gid));
394 		}
395 		dbgprintf("gid %d obtained for group '%s'\n", (int) pgBuf->gr_gid, szName);
396 	}
397 
398 	skipWhiteSpace(pp); /* skip over any whitespace */
399 
400 finalize_it:
401 	free(stringBuf);
402 	RETiRet;
403 }
404 
405 
406 /* extract a username and return its uid.
407  * rgerhards, 2007-07-17
408  */
doGetUID(uchar ** pp,rsRetVal (* pSetHdlr)(void *,uid_t),void * pVal)409 static rsRetVal doGetUID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
410 {
411 	struct passwd *ppwBuf;
412 	struct passwd pwBuf;
413 	DEFiRet;
414 	uchar szName[256];
415 	char stringBuf[2048];	/* I hope this is large enough... */
416 
417 	assert(pp != NULL);
418 	assert(*pp != NULL);
419 
420 	if(getSubString(pp, (char*) szName, sizeof(szName), ' ')  != 0) {
421 		if(loadConf->globals.abortOnIDResolutionFail) {
422 			fprintf(stderr, "could not extract user name: %s\n", (char*)szName);
423 			exit(1); /* good exit */
424 		} else {
425 			LogError(0, RS_RET_NOT_FOUND, "could not extract user name");
426 			ABORT_FINALIZE(RS_RET_NOT_FOUND);
427 		}
428 	}
429 
430 	getpwnam_r((char*)szName, &pwBuf, stringBuf, sizeof(stringBuf), &ppwBuf);
431 
432 	if(ppwBuf == NULL) {
433 		if(loadConf->globals.abortOnIDResolutionFail) {
434 			fprintf(stderr, "ID for user '%s' could not be found or error\n", (char*)szName);
435 			exit(1); /* good exit */
436 		} else {
437 			LogError(0, RS_RET_NOT_FOUND, "ID for user '%s' could not be found or error", (char*)szName);
438 			iRet = RS_RET_NOT_FOUND;
439 		}
440 	} else {
441 		if(pSetHdlr == NULL) {
442 			/* we should set value directly to var */
443 			*((uid_t*)pVal) = ppwBuf->pw_uid;
444 		} else {
445 			/* we set value via a set function */
446 			CHKiRet(pSetHdlr(pVal, ppwBuf->pw_uid));
447 		}
448 		dbgprintf("uid %d obtained for user '%s'\n", (int) ppwBuf->pw_uid, szName);
449 	}
450 
451 	skipWhiteSpace(pp); /* skip over any whitespace */
452 
453 finalize_it:
454 	RETiRet;
455 }
456 
457 
458 /* Parse and process an binary cofig option. pVal must be
459  * a pointer to an integer which is to receive the option
460  * value.
461  * rgerhards, 2007-07-15
462  */
doBinaryOptionLine(uchar ** pp,rsRetVal (* pSetHdlr)(void *,int),void * pVal)463 static rsRetVal doBinaryOptionLine(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal)
464 {
465 	int iOption;
466 	DEFiRet;
467 
468 	assert(pp != NULL);
469 	assert(*pp != NULL);
470 
471 	if((iOption = doParseOnOffOption(pp)) == -1)
472 		return RS_RET_ERR;	/* nothing left to do */
473 
474 	if(pSetHdlr == NULL) {
475 		/* we should set value directly to var */
476 		*((int*)pVal) = iOption;
477 	} else {
478 		/* we set value via a set function */
479 		CHKiRet(pSetHdlr(pVal, iOption));
480 	}
481 
482 	skipWhiteSpace(pp); /* skip over any whitespace */
483 
484 finalize_it:
485 	RETiRet;
486 }
487 
488 
489 /* parse a whitespace-delimited word from the provided string. This is a
490  * helper function for a number of syntaxes. The parsed value is returned
491  * in ppStrB (which must be provided by caller).
492  * rgerhards, 2008-02-14
493  */
494 static rsRetVal
getWord(uchar ** pp,cstr_t ** ppStrB)495 getWord(uchar **pp, cstr_t **ppStrB)
496 {
497 	DEFiRet;
498 	uchar *p;
499 
500 	assert(pp != NULL);
501 	assert(*pp != NULL);
502 	assert(ppStrB != NULL);
503 
504 	CHKiRet(cstrConstruct(ppStrB));
505 
506 	skipWhiteSpace(pp); /* skip over any whitespace */
507 
508 	/* parse out the word */
509 	p = *pp;
510 
511 	while(*p && !isspace((int) *p)) {
512 		CHKiRet(cstrAppendChar(*ppStrB, *p++));
513 	}
514 	cstrFinalize(*ppStrB);
515 
516 	*pp = p;
517 
518 finalize_it:
519 	RETiRet;
520 }
521 
522 
523 /* Parse and a word config line option. A word is a consequtive
524  * sequence of non-whitespace characters. pVal must be
525  * a pointer to a string which is to receive the option
526  * value. The returned string must be freed by the caller.
527  * rgerhards, 2007-09-07
528  * To facilitate multiple instances of the same command line
529  * directive, doGetWord() now checks if pVal is already a
530  * non-NULL pointer. If so, we assume it was created by a previous
531  * incarnation and is automatically freed. This happens only when
532  * no custom handler is defined. If it is, the customer handler
533  * must do the cleanup. I have checked and this was al also memory
534  * leak with some code. Obviously, not a large one. -- rgerhards, 2007-12-20
535  * Just to clarify: if pVal is parsed to a custom handler, this handler
536  * is responsible for freeing pVal. -- rgerhards, 2008-03-20
537  */
doGetWord(uchar ** pp,rsRetVal (* pSetHdlr)(void *,uchar *),void * pVal)538 static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void *pVal)
539 {
540 	DEFiRet;
541 	cstr_t *pStrB = NULL;
542 	uchar *pNewVal;
543 
544 	assert(pp != NULL);
545 	assert(*pp != NULL);
546 
547 	CHKiRet(getWord(pp, &pStrB));
548 	CHKiRet(cstrConvSzStrAndDestruct(&pStrB, &pNewVal, 0));
549 
550 	DBGPRINTF("doGetWord: get newval '%s' (len %d), hdlr %p\n",
551 		  pNewVal, (int) ustrlen(pNewVal), pSetHdlr);
552 	/* we got the word, now set it */
553 	if(pSetHdlr == NULL) {
554 		/* we should set value directly to var */
555 		if(*((uchar**)pVal) != NULL)
556 			free(*((uchar**)pVal)); /* free previous entry */
557 		*((uchar**)pVal) = pNewVal; /* set new one */
558 	} else {
559 		/* we set value via a set function */
560 		CHKiRet(pSetHdlr(pVal, pNewVal));
561 	}
562 
563 	skipWhiteSpace(pp); /* skip over any whitespace */
564 
565 finalize_it:
566 	if(iRet != RS_RET_OK) {
567 		if(pStrB != NULL)
568 			cstrDestruct(&pStrB);
569 	}
570 
571 	RETiRet;
572 }
573 
574 
575 /* parse a syslog name from the string. This is the generic code that is
576  * called by the facility/severity functions. Note that we do not check the
577  * validity of numerical values, something that should probably change over
578  * time (TODO). -- rgerhards, 2008-02-14
579  */
580 static rsRetVal
doSyslogName(uchar ** pp,rsRetVal (* pSetHdlr)(void *,int),void * pVal,syslogName_t * pNameTable)581 doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int),
582 	  	    void *pVal, syslogName_t *pNameTable)
583 {
584 	DEFiRet;
585 	cstr_t *pStrB;
586 	int iNewVal;
587 
588 	assert(pp != NULL);
589 	assert(*pp != NULL);
590 
591 	CHKiRet(getWord(pp, &pStrB)); /* get word */
592 	iNewVal = decodeSyslogName(cstrGetSzStrNoNULL(pStrB), pNameTable);
593 
594 	if(pSetHdlr == NULL) {
595 		/* we should set value directly to var */
596 		*((int*)pVal) = iNewVal; /* set new one */
597 	} else {
598 		/* we set value via a set function */
599 		CHKiRet(pSetHdlr(pVal, iNewVal));
600 	}
601 
602 	skipWhiteSpace(pp); /* skip over any whitespace */
603 
604 finalize_it:
605 	if(pStrB != NULL)
606 		rsCStrDestruct(&pStrB);
607 
608 	RETiRet;
609 }
610 
611 
612 /* Implements the facility syntax.
613  * rgerhards, 2008-02-14
614  */
615 static rsRetVal
doFacility(uchar ** pp,rsRetVal (* pSetHdlr)(void *,int),void * pVal)616 doFacility(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal)
617 {
618 	DEFiRet;
619 	iRet = doSyslogName(pp, pSetHdlr, pVal, syslogFacNames);
620 	RETiRet;
621 }
622 
623 
624 static rsRetVal
doGoneAway(uchar ** pp,rsRetVal (* pSetHdlr)(void *,int),void * pVal)625 doGoneAway(__attribute__((unused)) uchar **pp,
626 	   __attribute__((unused)) rsRetVal (*pSetHdlr)(void*, int),
627 	   __attribute__((unused)) void *pVal)
628 {
629 	parser_warnmsg("config directive is no longer supported -- ignored");
630 	return RS_RET_CMD_GONE_AWAY;
631 }
632 
633 /* Implements the severity syntax.
634  * rgerhards, 2008-02-14
635  */
636 static rsRetVal
doSeverity(uchar ** pp,rsRetVal (* pSetHdlr)(void *,int),void * pVal)637 doSeverity(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal)
638 {
639 	DEFiRet;
640 	iRet = doSyslogName(pp, pSetHdlr, pVal, syslogPriNames);
641 	RETiRet;
642 }
643 
644 
645 /* --------------- END functions for handling canned syntaxes --------------- */
646 
647 /* destructor for cslCmdHdlr
648  * pThis is actually a cslCmdHdlr_t, but we do not cast it as all we currently
649  * need to do is free it.
650  */
cslchDestruct(void * pThis)651 static rsRetVal cslchDestruct(void *pThis)
652 {
653 	assert(pThis != NULL);
654 	free(pThis);
655 
656 	return RS_RET_OK;
657 }
658 
659 
660 /* constructor for cslCmdHdlr
661  */
cslchConstruct(cslCmdHdlr_t ** ppThis)662 static rsRetVal cslchConstruct(cslCmdHdlr_t **ppThis)
663 {
664 	cslCmdHdlr_t *pThis;
665 	DEFiRet;
666 
667 	assert(ppThis != NULL);
668 	if((pThis = calloc(1, sizeof(cslCmdHdlr_t))) == NULL) {
669 		ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
670 	}
671 
672 finalize_it:
673 	*ppThis = pThis;
674 	RETiRet;
675 }
676 
677 /* destructor for linked list keys. As we do not use any dynamic memory,
678  * we simply return. However, this entry point must be defined for the
679  * linkedList class to make sure we have not forgotten a destructor.
680  * rgerhards, 2007-11-21
681  */
cslchKeyDestruct(void * pData)682 static rsRetVal cslchKeyDestruct(void __attribute__((unused)) *pData)
683 {
684 	return RS_RET_OK;
685 }
686 
687 
688 /* Key compare operation for linked list class. This compares two
689  * owner cookies (void *).
690  * rgerhards, 2007-11-21
691  */
cslchKeyCompare(void * pKey1,void * pKey2)692 static int cslchKeyCompare(void *pKey1, void *pKey2)
693 {
694 	if(pKey1 == pKey2)
695 		return 0;
696 	else
697 		if(pKey1 < pKey2)
698 			return -1;
699 		else
700 			return 1;
701 }
702 
703 
704 /* set data members for this object
705  */
cslchSetEntry(cslCmdHdlr_t * pThis,ecslCmdHdrlType eType,rsRetVal (* pHdlr)(),void * pData,int * permitted)706 static rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData,
707 int *permitted)
708 {
709 	assert(pThis != NULL);
710 	assert(eType != eCmdHdlrInvalid);
711 
712 	pThis->eType = eType;
713 	pThis->cslCmdHdlr = pHdlr;
714 	pThis->pData = pData;
715 	pThis->permitted = permitted;
716 
717 	return RS_RET_OK;
718 }
719 
720 
721 /* call the specified handler
722  */
cslchCallHdlr(cslCmdHdlr_t * pThis,uchar ** ppConfLine)723 static rsRetVal cslchCallHdlr(cslCmdHdlr_t *pThis, uchar **ppConfLine)
724 {
725 	DEFiRet;
726 	rsRetVal (*pHdlr)() = NULL;
727 	assert(pThis != NULL);
728 	assert(ppConfLine != NULL);
729 
730 	switch(pThis->eType) {
731 	case eCmdHdlrCustomHandler:
732 		pHdlr = doCustomHdlr;
733 		break;
734 	case eCmdHdlrUID:
735 		pHdlr = doGetUID;
736 		break;
737 	case eCmdHdlrGID:
738 		pHdlr = doGetGID;
739 		break;
740 	case eCmdHdlrBinary:
741 		pHdlr = doBinaryOptionLine;
742 		break;
743 	case eCmdHdlrFileCreateMode:
744 		pHdlr = doFileCreateMode;
745 		break;
746 	case eCmdHdlrInt:
747 		pHdlr = doGetInt;
748 		break;
749 	case eCmdHdlrSize:
750 		pHdlr = doGetSize;
751 		break;
752 	case eCmdHdlrGetChar:
753 		pHdlr = doGetChar;
754 		break;
755 	case eCmdHdlrFacility:
756 		pHdlr = doFacility;
757 		break;
758 	case eCmdHdlrSeverity:
759 		pHdlr = doSeverity;
760 		break;
761 	case eCmdHdlrGetWord:
762 		pHdlr = doGetWord;
763 		break;
764 	case eCmdHdlrGoneAway:
765 		pHdlr = doGoneAway;
766 		break;
767 	/* some non-legacy handler (used in v6+ solely) */
768 	case eCmdHdlrInvalid:
769 	case eCmdHdlrNonNegInt:
770 	case eCmdHdlrPositiveInt:
771 	case eCmdHdlrString:
772 	case eCmdHdlrArray:
773 	case eCmdHdlrQueueType:
774 	default:
775 		dbgprintf("error: command handler type %d not implemented in legacy system\n", pThis->eType);
776 		iRet = RS_RET_NOT_IMPLEMENTED;
777 		goto finalize_it;
778 	}
779 
780 	/* we got a pointer to the handler, so let's call it */
781 	assert(pHdlr != NULL);
782 	CHKiRet(pHdlr(ppConfLine, pThis->cslCmdHdlr, pThis->pData));
783 
784 finalize_it:
785 	RETiRet;
786 }
787 
788 
789 /* ---------------------------------------------------------------------- *
790  * now come the handlers for cslCmd_t
791  * ---------------------------------------------------------------------- */
792 
793 /* destructor for a cslCmd list key (a string as of now)
794  */
cslcKeyDestruct(void * pData)795 static rsRetVal cslcKeyDestruct(void *pData)
796 {
797 	free(pData); /* we do not need to cast as all we do is free it anyway... */
798 	return RS_RET_OK;
799 }
800 
801 /* destructor for cslCmd
802  */
cslcDestruct(void * pData)803 static rsRetVal cslcDestruct(void *pData)
804 {
805 	cslCmd_t *pThis = (cslCmd_t*) pData;
806 
807 	assert(pThis != NULL);
808 
809 	llDestroy(&pThis->llCmdHdlrs);
810 	free(pThis);
811 
812 	return RS_RET_OK;
813 }
814 
815 
816 /* constructor for cslCmd
817  */
cslcConstruct(cslCmd_t ** ppThis,int bChainingPermitted)818 static rsRetVal cslcConstruct(cslCmd_t **ppThis, int bChainingPermitted)
819 {
820 	cslCmd_t *pThis;
821 	DEFiRet;
822 
823 	assert(ppThis != NULL);
824 	if((pThis = calloc(1, sizeof(cslCmd_t))) == NULL) {
825 		ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
826 	}
827 
828 	pThis->bChainingPermitted = bChainingPermitted;
829 
830 	CHKiRet(llInit(&pThis->llCmdHdlrs, cslchDestruct, cslchKeyDestruct, cslchKeyCompare));
831 
832 finalize_it:
833 	*ppThis = pThis;
834 	RETiRet;
835 }
836 
837 
838 /* add a handler entry to a known command
839  */
cslcAddHdlr(cslCmd_t * pThis,ecslCmdHdrlType eType,rsRetVal (* pHdlr)(),void * pData,void * pOwnerCookie,int * permitted)840 static rsRetVal cslcAddHdlr(cslCmd_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData,
841 void *pOwnerCookie, int *permitted)
842 {
843 	DEFiRet;
844 	cslCmdHdlr_t *pCmdHdlr = NULL;
845 
846 	assert(pThis != NULL);
847 
848 	CHKiRet(cslchConstruct(&pCmdHdlr));
849 	CHKiRet(cslchSetEntry(pCmdHdlr, eType, pHdlr, pData, permitted));
850 	CHKiRet(llAppend(&pThis->llCmdHdlrs, pOwnerCookie, pCmdHdlr));
851 
852 finalize_it:
853 	if(iRet != RS_RET_OK) {
854 		if(pHdlr != NULL)
855 			cslchDestruct(pCmdHdlr);
856 	}
857 
858 	RETiRet;
859 }
860 
861 
862 /* function that registers cfsysline handlers.
863  * The supplied pCmdName is copied and a new buffer is allocated. This
864  * buffer is automatically destroyed when the element is freed, the
865  * caller does not need to take care of that. The caller must, however,
866  * free pCmdName if he allocated it dynamically! -- rgerhards, 2007-08-09
867  * Parameter permitted has been added to support the v2 config system. With it,
868  * we can tell the legacy system (us here!) to check if a config directive is
869  * still permitted. For example, the v2 system will disable module global
870  * parameters if the are supplied via the native v2 callbacks. In order not
871  * to break exisiting modules, we have renamed the rgCfSysLinHdlr routine to
872  * version 2 and added a new one with the original name. It just calls the
873  * v2 function and supplies a "don't care (NULL)" pointer as this argument.
874  * rgerhards, 2012-06-26
875  */
regCfSysLineHdlr2(const uchar * pCmdName,int bChainingPermitted,ecslCmdHdrlType eType,rsRetVal (* pHdlr)(),void * pData,void * pOwnerCookie,int * permitted)876 rsRetVal regCfSysLineHdlr2(const uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(),
877 void *pData, void *pOwnerCookie, int *permitted)
878 {
879 	DEFiRet;
880 	cslCmd_t *pThis;
881 	uchar *pMyCmdName;
882 
883 	iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pThis);
884 	if(iRet == RS_RET_NOT_FOUND) {
885 		/* new command */
886 		CHKiRet(cslcConstruct(&pThis, bChainingPermitted));
887 		CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie, permitted)) {
888 			cslcDestruct(pThis);
889 			FINALIZE;
890 		}
891 		/* important: add to list, AFTER everything else is OK. Else
892 		 * we mess up things in the error case.
893 		 */
894 		if((pMyCmdName = (uchar*) strdup((char*)pCmdName)) == NULL) {
895 			cslcDestruct(pThis);
896 			ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
897 		}
898 		CHKiRet_Hdlr(llAppend(&llCmdList, pMyCmdName, (void*) pThis)) {
899 			cslcDestruct(pThis);
900 			FINALIZE;
901 		}
902 	} else {
903 		/* command already exists, are we allowed to chain? */
904 		if(pThis->bChainingPermitted == 0 || bChainingPermitted == 0) {
905 			ABORT_FINALIZE(RS_RET_CHAIN_NOT_PERMITTED);
906 		}
907 		CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie, permitted)) {
908 			cslcDestruct(pThis);
909 			FINALIZE;
910 		}
911 	}
912 
913 finalize_it:
914 	RETiRet;
915 }
916 
regCfSysLineHdlr(const uchar * pCmdName,int bChainingPermitted,ecslCmdHdrlType eType,rsRetVal (* pHdlr)(),void * pData,void * pOwnerCookie)917 rsRetVal regCfSysLineHdlr(const uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(),
918 void *pData, void *pOwnerCookie)
919 {
920 	DEFiRet;
921 	iRet = regCfSysLineHdlr2(pCmdName, bChainingPermitted, eType, pHdlr, pData, pOwnerCookie, NULL);
922 	RETiRet;
923 }
924 
925 
unregCfSysLineHdlrs(void)926 rsRetVal unregCfSysLineHdlrs(void)
927 {
928 	return llDestroy(&llCmdList);
929 }
930 
931 
932 /* helper function for unregCfSysLineHdlrs4Owner(). This is used to see if there is
933  * a handler of this owner inside the element and, if so, remove it. Please note that
934  * it keeps track of a pointer to the last linked list entry, as this is needed to
935  * remove an entry from the list.
936  * rgerhards, 2007-11-21
937  */
DEFFUNC_llExecFunc(unregHdlrsHeadExec)938 DEFFUNC_llExecFunc(unregHdlrsHeadExec)
939 {
940 	DEFiRet;
941 	cslCmd_t *pListHdr = (cslCmd_t*) pData;
942 	int iNumElts;
943 
944 	/* first find element */
945 	CHKiRet(llFindAndDelete(&(pListHdr->llCmdHdlrs), pParam));
946 
947 	/* now go back and check how many elements are left */
948 	CHKiRet(llGetNumElts(&(pListHdr->llCmdHdlrs), &iNumElts));
949 
950 	if(iNumElts == 0) {
951 		/* nothing left in header, so request to delete it */
952 		iRet = RS_RET_OK_DELETE_LISTENTRY;
953 	}
954 
955 finalize_it:
956 	RETiRet;
957 }
958 /* unregister and destroy cfSysLineHandlers for a specific owner. This method is
959  * most importantly used before unloading a loadable module providing some handlers.
960  * The full list of handlers is searched. If the to-be removed handler was the only
961  * handler for a directive name, the directive header, too, is deleted.
962  * rgerhards, 2007-11-21
963  */
unregCfSysLineHdlrs4Owner(void * pOwnerCookie)964 rsRetVal unregCfSysLineHdlrs4Owner(void *pOwnerCookie)
965 {
966 	DEFiRet;
967 	/* we need to walk through all directive names, as the linked list
968 	 * class does not provide a way to just search the lower-level handlers.
969 	 */
970 	iRet = llExecFunc(&llCmdList, unregHdlrsHeadExec, pOwnerCookie);
971 	if(iRet == RS_RET_NOT_FOUND) {
972 		/* It is not considered an error if a module had no
973 		   hanlers registered. */
974 		iRet = RS_RET_OK;
975 	}
976 
977 	RETiRet;
978 }
979 
980 
981 /* process a cfsysline command (based on handler structure)
982  * param "p" is a pointer to the command line after the command. Should be
983  * updated.
984  */
processCfSysLineCommand(uchar * pCmdName,uchar ** p)985 rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p)
986 {
987 	DEFiRet;
988 	rsRetVal iRetLL; /* for linked list handling */
989 	cslCmd_t *pCmd;
990 	cslCmdHdlr_t *pCmdHdlr;
991 	linkedListCookie_t llCookieCmdHdlr;
992 	uchar *pHdlrP; /* the handler's private p (else we could only call one handler) */
993 	int bWasOnceOK; /* was the result of an handler at least once RS_RET_OK? */
994 	uchar *pOKp = NULL; /* returned conf line pointer when it was OK */
995 
996 	iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pCmd);
997 
998 	if(iRet == RS_RET_NOT_FOUND) {
999 		LogError(0, RS_RET_NOT_FOUND, "invalid or yet-unknown config file command '%s' - "
1000 			"have you forgotten to load a module?", pCmdName);
1001 	}
1002 
1003 	if(iRet != RS_RET_OK)
1004 		goto finalize_it;
1005 
1006 	llCookieCmdHdlr = NULL;
1007 	bWasOnceOK = 0;
1008 	while((iRetLL = llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr)) == RS_RET_OK) {
1009 		/* for the time being, we ignore errors during handlers. The
1010 		 * reason is that handlers are independent. An error in one
1011 		 * handler does not necessarily mean that another one will
1012 		 * fail, too. Later, we might add a config variable to control
1013 		 * this behaviour (but I am not sure if that is really
1014 		 * necessary). -- rgerhards, 2007-07-31
1015 		 */
1016 		pHdlrP = *p;
1017 		if(pCmdHdlr->permitted != NULL && !*(pCmdHdlr->permitted)) {
1018 			LogError(0, RS_RET_PARAM_NOT_PERMITTED, "command '%s' is currently not "
1019 				"permitted - did you already set it via a RainerScript command (v6+ config)?",
1020 				pCmdName);
1021 			ABORT_FINALIZE(RS_RET_PARAM_NOT_PERMITTED);
1022 		} else if((iRet = cslchCallHdlr(pCmdHdlr, &pHdlrP)) == RS_RET_OK) {
1023 			bWasOnceOK = 1;
1024 			pOKp = pHdlrP;
1025 		}
1026 	}
1027 
1028 	if(bWasOnceOK == 1) {
1029 		*p = pOKp;
1030 		iRet = RS_RET_OK;
1031 	}
1032 
1033 	if(iRetLL != RS_RET_END_OF_LINKEDLIST)
1034 		iRet = iRetLL;
1035 
1036 finalize_it:
1037 	RETiRet;
1038 }
1039 
1040 
1041 /* debug print the command handler structure
1042  */
dbgPrintCfSysLineHandlers(void)1043 void dbgPrintCfSysLineHandlers(void)
1044 {
1045 	cslCmd_t *pCmd;
1046 	cslCmdHdlr_t *pCmdHdlr;
1047 	linkedListCookie_t llCookieCmd;
1048 	linkedListCookie_t llCookieCmdHdlr;
1049 	uchar *pKey;
1050 
1051 	dbgprintf("Sytem Line Configuration Commands:\n");
1052 	llCookieCmd = NULL;
1053 	while(llGetNextElt(&llCmdList, &llCookieCmd, (void*)&pCmd) == RS_RET_OK) {
1054 		llGetKey(llCookieCmd, (void*) &pKey); /* TODO: using the cookie is NOT clean! */
1055 		dbgprintf("\tCommand '%s':\n", pKey);
1056 		llCookieCmdHdlr = NULL;
1057 		while(llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr) == RS_RET_OK) {
1058 			dbgprintf("\t\ttype : %d\n", pCmdHdlr->eType);
1059 			dbgprintf("\t\tpData: 0x%lx\n", (unsigned long) pCmdHdlr->pData);
1060 			dbgprintf("\t\tHdlr : 0x%lx\n", (unsigned long) pCmdHdlr->cslCmdHdlr);
1061 			dbgprintf("\t\tOwner: 0x%lx\n", (unsigned long) llCookieCmdHdlr->pKey);
1062 			dbgprintf("\n");
1063 		}
1064 	}
1065 	dbgprintf("\n");
1066 }
1067 
1068 
1069 /* our init function. TODO: remove once converted to a class
1070  */
1071 rsRetVal
cfsyslineInit(void)1072 cfsyslineInit(void)
1073 {
1074 	DEFiRet;
1075 	CHKiRet(objGetObjInterface(&obj));
1076 
1077 	CHKiRet(llInit(&llCmdList, cslcDestruct, cslcKeyDestruct, strcasecmp));
1078 
1079 finalize_it:
1080 	RETiRet;
1081 }
1082