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