1 /* imbatchreport.c
2 *
3 * This is the input module for reading full text file data. A text file is a
4 * non-binary file who's lines are delimited by the \n character. The file is
5 * treated as a single message. An optional structured data can be written at
6 * the end of the file.
7 *
8 * No state file are used as it should only grow with time. Instead the state
9 * is managed using the name of the file. A "glob" allows the module to identify
10 * "to be treated" files. The module can be configured either to deleted the
11 * the file on success either to rename the file on success. The size limit is
12 * fixed by rsyslog max message size global parameter. All files larger than this
13 * limit produce a message which references it as "too large" and its new location
14 * The "too large" files are also renamed to keep them available.
15 *
16 * This modules allows to centralize batch reports with other standard logs and
17 * performance monitoring in a single repository (ElasticSearch, HDFS, ...). This
18 * centralization helps to identify cause of global performance issues.
19 *
20 * Work originally begun on 2014-07-01 by Philippe Duveau @ Pari Mutuel Urbain
21 *
22 * This file is contribution of rsyslog.
23 *
24 * Licensed under the Apache License, Version 2.0 (the "License");
25 * you may not use this file except in compliance with the License.
26 * You may obtain a copy of the License at
27 *
28 * http://www.apache.org/licenses/LICENSE-2.0
29 * -or-
30 * see COPYING.ASL20 in the source distribution
31 *
32 * Unless required by applicable law or agreed to in writing, software
33 * distributed under the License is distributed on an "AS IS" BASIS,
34 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35 * See the License for the specific language governing permissions and
36 * limitations under the License.
37 */
38 #include "config.h"
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <assert.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <pthread.h> /* do NOT remove: will soon be done by the module generation macros */
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <glob.h>
49 #include <fnmatch.h>
50 #ifdef HAVE_SYS_STAT_H
51 # include <sys/stat.h>
52 #endif
53 #include "rsyslog.h" /* error codes etc... */
54 #include "dirty.h"
55 #include "cfsysline.h" /* access to config file objects */
56 #include "module-template.h"
57 #include "srUtils.h" /* some utility functions */
58 #include "msg.h"
59 #include "errmsg.h"
60 #include "glbl.h"
61 #include "datetime.h"
62 #include "unicode-helper.h"
63 #include "prop.h"
64 #include "stringbuf.h"
65 #include "ruleset.h"
66 #include "ratelimit.h"
67
68 #include <regex.h>
69
70 MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
71 MODULE_TYPE_NOKEEP
72 MODULE_CNFNAME("imbatchreport")
73
74 /* defines for freebsd */
75 #ifndef O_LARGEFILE
76 #define O_LARGEFILE 0
77 #endif
78
79 /* Module static data */
80 DEF_IMOD_STATIC_DATA /* must be present, starts static data */
81 DEFobjCurrIf(glbl)
82 DEFobjCurrIf(ruleset)
83 DEFobjCurrIf(datetime)
84 DEFobjCurrIf(prop)
85
86 #define SRUCTDATA_BUFFER_LEN 150
87 #define READ_BUFFER_LEN 512
88 #define FILE_TOO_LARGE "File too large : "
89 #define FILE_TOO_LARGE_LEN sizeof(FILE_TOO_LARGE)-1
90
91 #define DFLT_PollInterval 10
92
93 #define ADD_METADATA_UNSPECIFIED -1
94
95 typedef enum action_batchreport_t {
96 action_nothing,
97 action_rename,
98 action_delete
99 } action_batchreport_t;
100
101 struct instanceConf_s {
102 uchar *pszFollow_glob;
103 uchar *pszDirName;
104 uchar *pszFileBaseName;
105 uchar *pszTag;
106 int lenTag;
107 uchar *pszTSk;
108 int lenTSk;
109 uchar *pszProgk;
110 int lenProgk;
111 int must_stop;
112 uchar *pszBindRuleset;
113 int bDedupSpace;
114 int iFacility;
115 int iSeverity;
116 char *ff_regex;
117 /* Full treatment : this should contain a regex applied on filename. The matching
118 part is then replaced with ff_replace to put the file out of scan criteria */
119 regex_t ff_preg;
120
121 char *ff_rename;
122 int len_rename;
123
124 char *ff_reject;
125 int len_reject;
126
127 int filename_oversize;
128 ruleset_t *pBindRuleset;
129 /* ruleset to bind listener to (use system default if unspecified) */
130
131 ratelimit_t *ratelimiter;
132
133 struct instanceConf_s *next;
134
135 action_batchreport_t action;
136
137 char *pszNewFName;
138 int fd;
139
140 sbool goon;
141 };
142
143 /* global configuration variables */
144 static struct {
145 uchar *hostname;
146 size_t lhostname;
147
148 char *msg_buffer;
149 size_t max_msg_size;
150
151 instanceConf_t *root;
152
153 /* number of seconds to sleep when there was no file activity */
154 int iPollInterval;
155 } fixedModConf;
156
157 /* config variables */
158 struct modConfData_s {
159 rsconf_t *pConf; /* our overall config object */
160 };
161
162 static prop_t *pInputName = NULL;
163 /* there is only one global inputName for all messages generated by this input */
164
165 /* module-global parameters */
166 static struct cnfparamdescr modpdescr[] = {
167 { "pollinginterval", eCmdHdlrPositiveInt, 0 },
168 };
169 static struct cnfparamblk modpblk =
170 { CNFPARAMBLK_VERSION,
171 sizeof(modpdescr)/sizeof(struct cnfparamdescr),
172 modpdescr
173 };
174
175 /* input instance parameters */
176 static struct cnfparamdescr inppdescr[] = {
177 { "reports", eCmdHdlrString, CNFPARAM_REQUIRED },
178 { "tag", eCmdHdlrString, CNFPARAM_REQUIRED },
179 { "programkey", eCmdHdlrString, 0 },
180 { "timestampkey", eCmdHdlrString, 0 },
181 { "deduplicatespace", eCmdHdlrBinary, 0},
182 { "rename", eCmdHdlrString, 0 },
183 { "delete", eCmdHdlrString, 0 },
184 { "severity", eCmdHdlrSeverity, 0 },
185 { "facility", eCmdHdlrFacility, 0 },
186 { "ruleset", eCmdHdlrString, 0 },
187 };
188 static struct cnfparamblk inppblk =
189 { CNFPARAMBLK_VERSION,
190 sizeof(inppdescr)/sizeof(struct cnfparamdescr),
191 inppdescr
192 };
193
194 #include "im-helper.h" /* must be included AFTER the type definitions! */
195
196 /* enqueue the read file line as a message. The provided string is
197 * not freed - thuis must be done by the caller.
198 */
enqMsg(instanceConf_t * pInst,smsg_t * pMsg)199 static rsRetVal enqMsg(instanceConf_t *pInst, smsg_t *pMsg)
200 {
201 DEFiRet;
202
203 MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY);
204 MsgSetInputName(pMsg, pInputName);
205 MsgSetHOSTNAME(pMsg, fixedModConf.hostname, (const int) fixedModConf.lhostname);
206 MsgSetTAG(pMsg, pInst->pszTag, pInst->lenTag);
207 MsgSetPROCID(pMsg, "-");
208 MsgSetMSGID(pMsg, "-");
209
210 pMsg->iFacility = pInst->iFacility >> 3;
211 pMsg->iSeverity = pInst->iSeverity;
212
213 MsgSetRuleset(pMsg, pInst->pBindRuleset);
214
215 CHKiRet(ratelimitAddMsg(pInst->ratelimiter, NULL, pMsg));
216
217 finalize_it:
218 RETiRet;
219 }
220
221 /* The following is a cancel cleanup handler for strmReadLine(). It is necessary in case
222 * strmReadLine() is cancelled while processing the stream. -- rgerhards, 2008-03-27
223 */
pollFileCancelCleanup(void * pArg)224 static void pollFileCancelCleanup(void *pArg)
225 {
226 instanceConf_t *ppInst = (instanceConf_t*) pArg;
227 if (ppInst->fd > 0)
228 close(ppInst->fd);
229 if (ppInst->pszNewFName)
230 free(ppInst->pszNewFName);
231 }
232
233 /* readAndSendFile try to read the file and send the message.
234 * @param pInst point to instance
235 * @param filename the file name
236 * @param fstate the file stat
237 */
readAndSendFile(instanceConf_t * pInst,char * filename,char * fpath,struct stat * fstat)238 static rsRetVal readAndSendFile(instanceConf_t *pInst, char *filename, char *fpath, struct stat *fstat)
239 {
240 smsg_t *pMsg = NULL;
241 size_t file_len, read_len = 0, sd_buf_l, msg_len = 0, idx = 0;
242 int last_is_space = 0;
243 struct timeval tvm;
244 uchar sd_buffer[SRUCTDATA_BUFFER_LEN];
245 uchar read_buffer[READ_BUFFER_LEN];
246
247 DEFiRet;
248
249 CHKiRet(msgConstruct(&pMsg));
250
251 msgAddMetadata(pMsg, (uchar*)"filename", (uchar*)filename);
252
253 /* get the file modification time : end of the batch*/
254 tvm.tv_sec = fstat->st_mtime;
255 tvm.tv_usec = 0;
256
257 file_len = lseek(pInst->fd, 0, SEEK_END);
258
259 MsgSetStructuredData(pMsg, "-");
260
261 /* Let's read the end of the file first and put it in the buffer for structuredData
262 * This will help to find the real end of the message
263 */
264 sd_buf_l = (file_len < SRUCTDATA_BUFFER_LEN) ? file_len : SRUCTDATA_BUFFER_LEN;
265
266 if (lseek(pInst->fd, file_len - sd_buf_l, SEEK_SET) >= 0) {
267 uchar *sdp = sd_buffer+sd_buf_l-1;
268 int nb_rm = 0; /* number of space chars removed */
269 size_t stdata_len = 0, t;
270 char *tmp;
271
272 if ((t=read(pInst->fd, sd_buffer, sd_buf_l)) != sd_buf_l) {
273 LogError(0, RS_RET_READ_ERR, "read end of file for structured data failed (%zu / %zu)",
274 t, sd_buf_l);
275 return RS_RET_READ_ERR;
276 }
277
278 /* let's trim the end */
279 for (; sdp > sd_buffer && (*sdp=='\n' || *sdp=='\t' || *sdp==' '); sdp--, sd_buf_l--)
280 file_len--;
281
282 if (sd_buf_l > 1 && *sdp == ']') {
283 stdata_len = 1;
284 /* it seems that we have structured data let find the begin */
285 for (; sdp > sd_buffer && *sdp!='['; sdp--, stdata_len++) {
286 if (*sdp == '\n') {
287 /* line feed not supported in structured data */
288 stdata_len--;
289 memmove(sdp, sdp+1, stdata_len);
290 nb_rm++;
291 }
292 }
293 if (*sdp == '[') {
294 /* we got a structured data */
295 DBGPRINTF("structured data : %.*s\n", (int)stdata_len, sdp);
296 MsgAddToStructuredData(pMsg, sdp, stdata_len);
297
298 /* extracting timestamp from structured data overwrite the file creation time */
299 if (pInst->pszTSk) {
300 uchar *field = (uchar*)strstr((char*)sdp, (char*)pInst->pszTSk), v;
301 if (field != NULL)
302 {
303 tvm.tv_sec = 0;
304 tvm.tv_usec = 0;
305 for (field += pInst->lenTSk; (v = *field ^ 0x30) <= 9; field++)
306 tvm.tv_sec = tvm.tv_sec*10 + v;
307 }
308 }
309
310 /* extracting program from structured data */
311 if (pInst->pszProgk) {
312 char *field = strstr((char*)sdp, (char*)pInst->pszProgk);
313 if (field != NULL)
314 {
315 tmp = field + pInst->lenProgk;
316 if ((field = strchr(tmp, '\"')) != NULL) {
317 *field = '\0';
318 MsgSetAPPNAME(pMsg, tmp);
319 }
320 }
321 }
322
323 /* let's trim until useful message end */
324 for (sdp--; sdp > sd_buffer && (*sdp=='\n' || *sdp=='\t' || *sdp==' '); sdp--)
325 nb_rm++;
326 }
327 }
328 /* computing the new file_len */
329 file_len -= nb_rm + stdata_len;
330 }
331
332 datetime.timeval2syslogTime(&tvm, &pMsg->tTIMESTAMP, TIME_IN_UTC);
333 pMsg->ttGenTime = tvm.tv_sec;
334
335 /* go back to beginning */
336 if (lseek(pInst->fd, 0, SEEK_SET) < 0) {
337 LogError(0, RS_RET_READ_ERR, "readAndSendFile : error while seeking to beginning.");
338 return RS_RET_READ_ERR;
339 }
340
341 /* Now read the file */
342 msg_len = 0;
343 while (msg_len < fixedModConf.max_msg_size && (read_len = read(pInst->fd, read_buffer,
344 (file_len > READ_BUFFER_LEN) ? READ_BUFFER_LEN : file_len)) > 0) {
345 file_len -= read_len;
346 idx = 0;
347 while (read_len > 0 && msg_len < fixedModConf.max_msg_size) {
348 switch (read_buffer[idx]){
349 case '\t':
350 case ' ':
351 /* this is to reduce consecutive spaces to only one */
352 if (!last_is_space)
353 fixedModConf.msg_buffer[msg_len++] = ' ';
354 /* if pInst->bDedupSpace is off last_is_space will never be true */
355 last_is_space = pInst->bDedupSpace;
356 break;
357 case '\n':
358 /* this is for trailing spaces */
359 if (last_is_space) msg_len--;
360 fixedModConf.msg_buffer[msg_len++] = '\\';
361 /* risk of overflow is managed by making buffer one char longer
362 * than fixedModConf.max_msg_size */
363 fixedModConf.msg_buffer[msg_len++] = 'n';
364 break;
365 default:
366 fixedModConf.msg_buffer[msg_len++] = read_buffer[idx];
367 last_is_space = 0;
368 }
369 idx++;
370 read_len--;
371 }
372 }
373
374 close(pInst->fd);
375 pInst->fd = 0;
376
377 if (file_len > 0 || read_len > 0) {
378 /* file is too large to be stored in one message */
379 memcpy(fixedModConf.msg_buffer, FILE_TOO_LARGE, FILE_TOO_LARGE_LEN);
380 msg_len = strlen(fpath);
381 memcpy(fixedModConf.msg_buffer + FILE_TOO_LARGE_LEN, fpath, msg_len);
382 msg_len += FILE_TOO_LARGE_LEN;
383 }
384
385 /* file is stored in the message */
386 MsgSetRawMsg(pMsg, fixedModConf.msg_buffer, msg_len);
387
388 MsgSetMSGoffs(pMsg, 0);
389 if ((iRet = enqMsg(pInst, pMsg)) == RS_RET_OK && (file_len > 0 || read_len > 0))
390 iRet = RS_RET_FILE_TOO_LARGE;
391
392 finalize_it:
393 RETiRet;
394 }
395
396 /* poll a glob */
pollFile(instanceConf_t * pInst)397 static void pollFile(instanceConf_t *pInst)
398 {
399 pInst->fd = 0;
400 glob_t glob_res;
401
402 pthread_cleanup_push(pollFileCancelCleanup, pInst);
403
404 DBGPRINTF("polling files : %s\n", pInst->pszFollow_glob);
405
406 /* We "glob" to find candidate regular file (or other) */
407 if (glob((char*)pInst->pszFollow_glob, GLOB_NOSORT, 0, &glob_res) != 0)
408 FINALIZE;
409
410 for (size_t i = 0; i < glob_res.gl_pathc && glbl.GetGlobalInputTermState() == 0; i++)
411 {
412 struct stat fstat;
413 rsRetVal ret;
414 char *filename = strrchr(glob_res.gl_pathv[i], '/');
415 if (filename)
416 filename++;
417 else
418 filename = glob_res.gl_pathv[i];
419 char *fpath = glob_res.gl_pathv[i];
420
421 /* let's verify that the file is a regular one */
422 if (!stat(fpath, &fstat) && S_ISREG(fstat.st_mode) )
423 {
424 regmatch_t matches[1];
425 int toolargeOrFailure = 0;
426
427 DBGPRINTF("Regular file found '%s')\n", fpath);
428
429 /* With this test we verify that we have conditions to remove the
430 * file from glob scope. If the regular expression not apply we
431 * can not rename it */
432 if (regexec(&pInst->ff_preg, fpath, 1, matches, 0)) {
433 pInst->must_stop = 1;
434 FINALIZE;
435 }
436
437 pInst->fd = open(fpath,
438 O_NOCTTY | O_RDONLY | O_NONBLOCK | O_LARGEFILE, 0);
439
440 if (pInst->fd <= 0)
441 {
442 /* file could be open unfortunately we will try each polling */
443 char errStr[512];
444 int eno = errno;
445 rs_strerror_r(eno, errStr, sizeof(errStr));
446 LogError(0, RS_RET_READ_ERR,"open the file %s failed with error %s", fpath, errStr);
447 continue;
448 }
449
450 /* let's read the file and send it to ouput */
451 ret = readAndSendFile(pInst, filename, fpath, &fstat);
452 /* is the file to large to be sent */
453 toolargeOrFailure = ret == RS_RET_FILE_TOO_LARGE;
454
455 if (ret != RS_RET_OK && ret != RS_RET_FILE_TOO_LARGE) {
456 LogError(0, ret, "The module could not manage the file %s", fpath);
457 toolargeOrFailure = 1;
458 }
459
460 if (pInst->action == action_rename || toolargeOrFailure)
461 {
462 pInst->pszNewFName = (char*)malloc(strlen(fpath)+
463 pInst->filename_oversize);
464 memcpy(pInst->pszNewFName, fpath, matches[0].rm_so);
465 strcpy((char*)pInst->pszNewFName + matches[0].rm_so,
466 (toolargeOrFailure) ? pInst->ff_reject : pInst->ff_rename);
467
468 if (rename(fpath, pInst->pszNewFName))
469 {
470 /* if the module can not rename the file, it must stop to avoid flooding
471 * but it keep chance to manage max files as possible
472 */
473 LogError(0, RS_RET_STREAM_DISABLED, "module stopped : was unable"
474 " to rename form %s to %s.", fpath , pInst->pszNewFName);
475 pInst->must_stop = 1;
476 }
477 else
478 DBGPRINTF("file %s sent and renamed to %s.\n", fpath, pInst->pszNewFName);
479 free(pInst->pszNewFName);
480 pInst->pszNewFName = NULL;
481 }
482 else
483 {
484 if (unlink(fpath))
485 {
486 /* if the module can not remove the file, it must stop to avoid flooding
487 * but it keep chance to manage max files as possible
488 */
489 LogError(0, RS_RET_STREAM_DISABLED, "module stopped : unable to delete %s.",
490 fpath);
491 pInst->must_stop = 1;
492 }
493 else
494 DBGPRINTF("file %s sent and deleted\n", fpath);
495 }
496 } /* if stat */
497 } /* for */
498
499 finalize_it:
500 globfree(&glob_res);
501
502 pthread_cleanup_pop(0);
503 }
504
addInstance(instanceConf_t * inst)505 static void addInstance(instanceConf_t *inst) {
506
507 if(fixedModConf.root == NULL) {
508 fixedModConf.root = inst;
509 } else {
510 fixedModConf.root->next = inst;
511 fixedModConf.root = inst;
512 }
513 }
514
515 /* create input instance, set default paramters, and
516 * add it to the list of instances.
517 */
518 static rsRetVal
createInstance(instanceConf_t ** pinst)519 createInstance(instanceConf_t **pinst)
520 {
521 instanceConf_t *inst;
522 DEFiRet;
523
524 *pinst = NULL;
525
526 CHKmalloc(inst = (instanceConf_t*)malloc(sizeof(instanceConf_t)));
527
528 inst->next = NULL;
529 inst->pBindRuleset = NULL;
530
531 inst->pszBindRuleset = NULL;
532 inst->pszFollow_glob = NULL;
533 inst->pszDirName = NULL;
534 inst->pszFileBaseName = NULL;
535 inst->pszTag = NULL;
536 inst->pszTSk = NULL;
537 inst->pszProgk = NULL;
538 inst->ff_regex = NULL;
539 inst->ff_rename = NULL;
540 inst->ff_reject = NULL;
541
542 inst->iSeverity = LOG_NOTICE;
543 inst->iFacility = LOG_LOCAL0;
544 inst->len_rename = 0;
545 inst->len_reject = 0;
546 inst->bDedupSpace = 1;
547 inst->goon = 0;
548 inst->ratelimiter = NULL;
549
550 inst->action = action_nothing;
551
552 inst->must_stop = 0;
553
554 *pinst = inst;
555 finalize_it:
556 RETiRet;
557 }
558
559 /* the basen(ame) buffer must be of size MAXFNAME
560 * returns the index of the slash in front of basename
561 */
getBasename(uchar * const __restrict__ basen,uchar * const __restrict__ path)562 static int getBasename(uchar *const __restrict__ basen, uchar *const __restrict__ path)
563 {
564 int i;
565 const int lenName = ustrlen(path);
566 for(i = lenName ; i >= 0 ; --i) {
567 if(path[i] == '/') {
568
569 if(i == lenName)
570 basen[0] = '\0';
571 else {
572 memcpy(basen, path+i+1, lenName-i);
573 }
574 break;
575 }
576 }
577 if (i == -1) {
578 memcpy(basen, path, lenName);
579 i = 0;
580 }
581 return i;
582 }
583
584 /* this function checks instance parameters and does some required pre-processing
585 * (e.g. split filename in path and actual name)
586 * Note: we do NOT use dirname()/basename() as they have portability problems.
587 */
checkInstance(instanceConf_t * inst)588 static rsRetVal checkInstance(instanceConf_t *inst)
589 {
590 char dirn[MAXFNAME];
591 uchar basen[MAXFNAME];
592 int i;
593 struct stat sb;
594 int r;
595 int eno;
596 char errStr[512], *s, *d;
597 regmatch_t matches[1];
598 DEFiRet;
599
600 i = getBasename(basen, inst->pszFollow_glob);
601 memcpy(dirn, inst->pszFollow_glob, i);
602 dirn[i] = '\0';
603 CHKmalloc(inst->pszFileBaseName = (uchar*) strdup((char*)basen));
604 CHKmalloc(inst->pszDirName = (uchar*) strdup(dirn));
605
606 if(dirn[0] == '\0') {
607 dirn[0] = '.';
608 dirn[1] = '\0';
609 }
610 r = stat(dirn, &sb);
611 if(r != 0) {
612 eno = errno;
613 rs_strerror_r(eno, errStr, sizeof(errStr));
614 LogError(0, RS_RET_CONFIG_ERROR, "Configured directory can not be stated '%s': %s",
615 dirn, errStr);
616 ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
617 }
618 if(!S_ISDIR(sb.st_mode)) {
619 LogError(0, RS_RET_CONFIG_ERROR, "Configured directory is NOT a directory : '%s'", dirn);
620 ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
621 }
622
623 if (stat((char*)inst->pszFollow_glob, &sb) == 0 && !S_ISREG(sb.st_mode)) {
624 LogError(0, RS_RET_CONFIG_ERROR, "Configured report is not a glob or a regular file.");
625 ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
626 }
627
628 for (s=(char*)inst->pszFollow_glob, d = dirn; *s; s++, d++)
629 *d = (*s != '*' && *s != '?') ? *s : '~';
630 *d = '\0';
631
632 if (regexec(&inst->ff_preg, dirn, 1, matches, 0)) {
633 LogError(0, RS_RET_CONFIG_ERROR,
634 "Regex does not match globed filenames: Instance ignored to avoid loops.");
635 ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
636 }
637 if (inst->action == action_rename) {
638 strcpy(dirn + matches[0].rm_so, inst->ff_rename);
639 if (fnmatch((char*)inst->pszFollow_glob, dirn, FNM_PATHNAME) == 0)
640 {
641 LogError(0, RS_RET_INVALID_PARAMS,
642 "Normal renaming leaves files in glob scope: Instance ignored to avoid loops.");
643 ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
644 }
645 }
646 strcpy(dirn + matches[0].rm_so, inst->ff_reject);
647 if (fnmatch((char*)inst->pszFollow_glob, dirn, FNM_PATHNAME) == 0)
648 {
649 LogError(0, RS_RET_INVALID_PARAMS,
650 "Reject renaming leaves files in glob scope: Instance ignored to avoid loops.");
651 ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
652 }
653 dbgprintf("instance checked");
654
655 finalize_it:
656 RETiRet;
657 }
658
freeInstance(instanceConf_t * inst)659 static void freeInstance(instanceConf_t *inst) {
660 if (inst == NULL)
661 return;
662
663 if (inst->pszBindRuleset) free(inst->pszBindRuleset);
664 if (inst->pszFollow_glob) free(inst->pszFollow_glob);
665 if (inst->pszDirName) free(inst->pszDirName);
666 if (inst->pszFileBaseName) free(inst->pszFileBaseName);
667 if (inst->pszTag) free(inst->pszTag);
668 if (inst->pszTSk) free(inst->pszTSk);
669 if (inst->pszProgk) free(inst->pszProgk);
670
671 if (inst->len_reject) regfree(&inst->ff_preg);
672
673 if (inst->ff_regex) free(inst->ff_regex);
674 if (inst->ff_rename) free(inst->ff_rename);
675 if (inst->ff_reject) free(inst->ff_reject);
676
677 if (inst->ratelimiter) ratelimitDestruct(inst->ratelimiter);
678 free(inst);
679 }
680
681 BEGINnewInpInst
682 struct cnfparamvals *pvals;
683 instanceConf_t *inst = NULL;
684 int i;
685 char *temp;
686 CODESTARTnewInpInst
687 DBGPRINTF("newInpInst (imbatchreport)\n");
688
689 pvals = nvlstGetParams(lst, &inppblk, NULL);
690 if(pvals == NULL) {
691 ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
692 }
693
694 DBGPRINTF("input param blk in imbatchreport:\n");
695 if(Debug) cnfparamsPrint(&inppblk, pvals);
696
697 CHKiRet(createInstance(&inst));
698
699 for(i = 0 ; i < inppblk.nParams ; ++i) {
700 if(!pvals[i].bUsed)
701 continue;
702 if(!strcmp(inppblk.descr[i].name, "reports")) {
703 inst->pszFollow_glob = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
704 } else if(!strcmp(inppblk.descr[i].name, "tag")) {
705 inst->pszTag = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
706 inst->lenTag = ustrlen(inst->pszTag);
707 } else if(!strcmp(inppblk.descr[i].name, "programkey")) {
708 inst->pszProgk = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
709 inst->lenProgk = ustrlen(inst->pszProgk) + 2;
710 } else if(!strcmp(inppblk.descr[i].name, "timestampkey")) {
711 inst->pszTSk = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
712 inst->lenTSk = ustrlen(inst->pszTSk) + 1;
713 } else if(!strcmp(inppblk.descr[i].name, "deduplicatespace")) {
714 inst->bDedupSpace = pvals[i].val.d.n;
715 } else if(!strcmp(inppblk.descr[i].name, "ruleset")) {
716 inst->pszBindRuleset = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
717 } else if(!strcmp(inppblk.descr[i].name, "severity")) {
718 inst->iSeverity = pvals[i].val.d.n;
719 } else if(!strcmp(inppblk.descr[i].name, "facility")) {
720 inst->iFacility = pvals[i].val.d.n;
721 } else if(!strcmp(inppblk.descr[i].name, "rename")) {
722 if (inst->action == action_delete)
723 {
724 LogError(0, RS_RET_PARAM_ERROR, "'rename' and 'delete' are exclusive !");
725 ABORT_FINALIZE(RS_RET_PARAM_ERROR);
726 }
727
728 inst->ff_regex = es_str2cstr(pvals[i].val.d.estr, NULL);
729
730 while ((temp = strchr(inst->ff_regex, '\t')) != NULL)
731 *temp = ' ';
732
733 inst->len_reject = 0;
734
735 if ((inst->ff_rename = strchr(inst->ff_regex, ' ')) != NULL ) {
736 *inst->ff_rename++ = '\0';
737
738 while (*inst->ff_rename == ' ') inst->ff_rename++;
739 if ((inst->ff_reject = strchr(inst->ff_rename, ' ')) != NULL ) {
740
741 *inst->ff_reject++ = '\0';
742 while (*inst->ff_reject == ' ') inst->ff_reject++;
743
744 temp = strchr(inst->ff_reject, ' ');
745 if (temp) *temp = '\0';
746
747 if (strcmp(inst->ff_rename, "-")){
748 inst->ff_rename = strdup(inst->ff_rename);
749 inst->len_rename = strlen(inst->ff_rename);
750 }else{
751 inst->ff_rename = strdup("");
752 inst->len_rename = 0;
753 }
754
755 inst->ff_reject = strdup(inst->ff_reject);
756 inst->len_reject = strlen(inst->ff_reject);
757
758 if (inst->len_reject && regcomp(&inst->ff_preg,
759 (char*)inst->ff_regex,
760 REG_EXTENDED))
761 {
762 inst->len_reject = 0;
763 LogError(0, RS_RET_SYNTAX_ERROR, "The first part of 'rename' "
764 "parameter does not contain a valid regex");
765 ABORT_FINALIZE(RS_RET_SYNTAX_ERROR);
766 }
767 }
768 }
769 if (inst->len_reject == 0)
770 {
771 LogError(0, RS_RET_PARAM_ERROR, "'rename' must specify THREE "
772 "parameters separated by spaces or tabs ! The second "
773 "parameter can be a null string to get this use a '-'.");
774 ABORT_FINALIZE(RS_RET_PARAM_ERROR);
775 }
776
777 inst->action = action_rename;
778
779 } else if(!strcmp(inppblk.descr[i].name, "delete")) {
780 if (inst->action == action_rename)
781 {
782 LogError(0, RS_RET_PARAM_ERROR, "'rename' and 'delete' are exclusive !");
783 ABORT_FINALIZE(RS_RET_PARAM_ERROR);
784 }
785
786 inst->ff_regex = es_str2cstr(pvals[i].val.d.estr, NULL);
787
788 while ((temp = strchr(inst->ff_regex, '\t')) != NULL)
789 *temp = ' ';
790
791 inst->len_reject = 0;
792
793 if ((inst->ff_reject = strchr(inst->ff_regex, ' ')) != NULL ) {
794 *inst->ff_reject++ = '\0';
795
796 while (*inst->ff_reject == ' ') inst->ff_reject++;
797
798 temp = strchr(inst->ff_reject, ' ');
799 if (temp) *temp = '\0';
800
801 inst->ff_reject = strdup(inst->ff_reject);
802 inst->len_reject = strlen(inst->ff_reject);
803
804 if (regcomp(&inst->ff_preg, (char*)inst->ff_regex, REG_EXTENDED))
805 {
806 inst->len_reject = 0;
807 LogError(0, RS_RET_SYNTAX_ERROR,
808 "The first part of 'delete' parameter does not contain a valid regex");
809 ABORT_FINALIZE(RS_RET_SYNTAX_ERROR);
810 }
811
812 }
813
814 if (inst->len_reject == 0)
815 {
816 LogError(0, RS_RET_PARAM_ERROR,
817 "'delete' must specify TWO parameters separated by spaces or tabs !");
818 ABORT_FINALIZE(RS_RET_PARAM_ERROR);
819 }
820 inst->action = action_delete;
821
822 } else {
823 dbgprintf("Configuration param '%s' non-handled\n", inppblk.descr[i].name);
824 }
825 }
826
827 if(inst->action == action_nothing) {
828 LogError(0, RS_RET_PARAM_NOT_PERMITTED, "either 'rename' or 'delete' must be set");
829 ABORT_FINALIZE(RS_RET_PARAM_NOT_PERMITTED);
830 }
831
832 inst->filename_oversize = (inst->len_rename > inst->len_reject) ? inst->len_rename : inst->len_reject;
833
834 CHKiRet(ratelimitNew(&inst->ratelimiter, "imbatchreport", (char*)inst->pszFollow_glob));
835
836 inst->goon = 1;
837
838 CHKiRet(checkInstance(inst));
839
840 finalize_it:
841 CODE_STD_FINALIZERnewInpInst
842
843 if (iRet == RS_RET_OK)
844 addInstance(inst);
845 else
846 freeInstance(inst);
847
848 cnfparamvalsDestruct(pvals, &inppblk);
849 ENDnewInpInst
850
851 BEGINbeginCnfLoad
852 CODESTARTbeginCnfLoad
853 pModConf->pConf = pConf;
854 fixedModConf.iPollInterval = DFLT_PollInterval;
855
856 fixedModConf.msg_buffer = NULL;
857 fixedModConf.root = NULL;
858 ENDbeginCnfLoad
859
860
861 BEGINsetModCnf
862 struct cnfparamvals *pvals = NULL;
863 int i;
864 CODESTARTsetModCnf
865 pvals = nvlstGetParams(lst, &modpblk, NULL);
866 if(pvals == NULL) {
867 LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module config parameters [module(...)]");
868 ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
869 }
870
871 if(Debug) {
872 dbgprintf("module (global) param blk for imbatchreport:\n");
873 cnfparamsPrint(&modpblk, pvals);
874 }
875
876 for(i = 0 ; i < modpblk.nParams ; ++i) {
877 if(!pvals[i].bUsed)
878 continue;
879 if(!strcmp(modpblk.descr[i].name, "pollinginterval")) {
880 fixedModConf.iPollInterval = (int) pvals[i].val.d.n;
881 } else {
882 dbgprintf("program error, non-handled "
883 "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
884 }
885 }
886 finalize_it:
887 if(pvals != NULL)
888 cnfparamvalsDestruct(pvals, &modpblk);
889 ENDsetModCnf
890
891
892 BEGINendCnfLoad
893 CODESTARTendCnfLoad
894 dbgprintf("polling interval is %d\n",
895 fixedModConf.iPollInterval);
896 ENDendCnfLoad
897
898
899 BEGINcheckCnf
900 instanceConf_t *inst;
901 CODESTARTcheckCnf
902 for(inst = fixedModConf.root ; inst != NULL ; inst = inst->next) {
903 std_checkRuleset(pModConf, inst);
904 }
905 if(fixedModConf.root == NULL) {
906 LogError(0, RS_RET_NO_LISTNERS,
907 "no files configured to be monitored - no input will be gathered");
908 iRet = RS_RET_NO_LISTNERS;
909 }
910 ENDcheckCnf
911
912 BEGINactivateCnf
913 CODESTARTactivateCnf
914 ENDactivateCnf
915
916 BEGINfreeCnf
917 instanceConf_t *inst, *del;
918 CODESTARTfreeCnf
919 for(inst = fixedModConf.root ; inst != NULL ; ) {
920 del = inst;
921 inst = inst->next;
922 freeInstance(del);
923 }
924 ENDfreeCnf
925
926 BEGINwillRun
927 CODESTARTwillRun
928 CHKiRet(prop.Construct(&pInputName));
929 CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imbatchreport"),
930 sizeof("imbatchreport") - 1));
931 CHKiRet(prop.ConstructFinalize(pInputName));
932
933 fixedModConf.max_msg_size = glbl.GetMaxLine();
934 DBGPRINTF("Max message len %zu\n", fixedModConf.max_msg_size);
935 CHKmalloc(fixedModConf.msg_buffer = (char*)malloc(fixedModConf.max_msg_size + 1));
936 finalize_it:
937 ENDwillRun
938
939 BEGINrunInput
940 CODESTARTrunInput
941 fixedModConf.hostname = glbl.GetLocalHostName();
942 fixedModConf.lhostname = ustrlen(fixedModConf.hostname);
943
944 while(glbl.GetGlobalInputTermState() == 0) {
945 instanceConf_t *pInst;
946 for(pInst = fixedModConf.root ; pInst != NULL ; pInst = pInst->next) {
947 if (pInst->goon) {
948 if(glbl.GetGlobalInputTermState() == 1)
949 break;
950 pollFile(pInst);
951 /* We got a major problem so */
952 pInst->goon = !pInst->must_stop;
953 }
954 }
955
956 if(glbl.GetGlobalInputTermState() == 0)
957 srSleep(fixedModConf.iPollInterval, 10);
958 }
959 DBGPRINTF("terminating upon request of rsyslog core\n");
960 RETiRet;
961 ENDrunInput
962
963 /* This function is called by the framework after runInput() has been terminated. It
964 * shall free any resources and prepare the module for unload.
965 */
966 BEGINafterRun
967 CODESTARTafterRun
968 if (fixedModConf.msg_buffer)
969 free(fixedModConf.msg_buffer);
970 if(pInputName != NULL)
971 prop.Destruct(&pInputName);
972 ENDafterRun
973
974
975 BEGINisCompatibleWithFeature
976 CODESTARTisCompatibleWithFeature
977 if(eFeat == sFEATURENonCancelInputTermination)
978 iRet = RS_RET_OK;
979 ENDisCompatibleWithFeature
980
981
982 /* The following entry points are defined in module-template.h.
983 * In general, they need to be present, but you do NOT need to provide
984 * any code here.
985 */
986 BEGINmodExit
987 CODESTARTmodExit
988
989 objRelease(datetime, CORE_COMPONENT);
990 objRelease(glbl, CORE_COMPONENT);
991 objRelease(prop, CORE_COMPONENT);
992 objRelease(ruleset, CORE_COMPONENT);
993
994 ENDmodExit
995
996
997 BEGINqueryEtryPt
998 CODESTARTqueryEtryPt
999 CODEqueryEtryPt_STD_IMOD_QUERIES
1000 CODEqueryEtryPt_STD_CONF2_QUERIES
1001 CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
1002 CODEqueryEtryPt_STD_CONF2_IMOD_QUERIES
1003 CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
1004 ENDqueryEtryPt
1005
1006
1007 /* modInit() is called once the module is loaded. It must perform all module-wide
1008 * initialization tasks. There are also a number of housekeeping tasks that the
1009 * framework requires. These are handled by the macros. Please note that the
1010 * complexity of processing is depending on the actual module. However, only
1011 * thing absolutely necessary should be done here. Actual app-level processing
1012 * is to be performed in runInput(). A good sample of what to do here may be to
1013 * set some variable defaults.
1014 */
1015 BEGINmodInit()
1016 CODESTARTmodInit
1017 *ipIFVersProvided = CURR_MOD_IF_VERSION;
1018 CODEmodInit_QueryRegCFSLineHdlr
1019 CHKiRet(objUse(glbl, CORE_COMPONENT));
1020 CHKiRet(objUse(datetime, CORE_COMPONENT));
1021 CHKiRet(objUse(ruleset, CORE_COMPONENT));
1022 CHKiRet(objUse(prop, CORE_COMPONENT));
1023 ENDmodInit
1024
1025
1026 static inline void
std_checkRuleset_genErrMsg(modConfData_t * modConf,instanceConf_t * inst)1027 std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf,
1028 instanceConf_t *inst)
1029 {
1030 LogError(0, NO_ERRCODE, "ruleset '%s' for %s not found - "
1031 "using default ruleset instead", inst->pszBindRuleset,
1032 inst->pszFollow_glob);
1033 }
1034