1 /* omtesting.c
2  *
3  * This module is a testing aid. It is not meant to be used in production. I have
4  * initially written it to introduce delays of custom length to action processing.
5  * This is needed for development of new message queueing methods. However, I think
6  * there are other uses for this module. For example, I can envision that it is a good
7  * thing to have an output module that requests a retry on every "n"th invocation
8  * and such things. I implement only what I need. But should further testing needs
9  * arise, it makes much sense to add them here.
10  *
11  * This module will become part of the CVS and the rsyslog project because I think
12  * it is a generally useful debugging, testing and development aid for everyone
13  * involved with rsyslog.
14  *
15  * CURRENT SUPPORTED COMMANDS:
16  *
17  * :omtesting:sleep <seconds> <microseconds>
18  *
19  * Must be specified exactly as above. Keep in mind microseconds are a millionth
20  * of a second!
21  *
22  * NOTE: read comments in module-template.h to understand how this file
23  *       works!
24  *
25  * Copyright 2007-2017 Rainer Gerhards and Adiscon GmbH.
26  *
27  * This file is part of rsyslog.
28  *
29  * Licensed under the Apache License, Version 2.0 (the "License");
30  * you may not use this file except in compliance with the License.
31  * You may obtain a copy of the License at
32  *
33  *       http://www.apache.org/licenses/LICENSE-2.0
34  *       -or-
35  *       see COPYING.ASL20 in the source distribution
36  *
37  * Unless required by applicable law or agreed to in writing, software
38  * distributed under the License is distributed on an "AS IS" BASIS,
39  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40  * See the License for the specific language governing permissions and
41  * limitations under the License.
42  */
43 #include "config.h"
44 #include "rsyslog.h"
45 #include <stdio.h>
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <time.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <assert.h>
52 #include <pthread.h>
53 #include "dirty.h"
54 #include "syslogd-types.h"
55 #include "module-template.h"
56 #include "conf.h"
57 #include "cfsysline.h"
58 #include "srUtils.h"
59 
60 MODULE_TYPE_OUTPUT
61 MODULE_TYPE_NOKEEP
62 MODULE_CNFNAME("omtesting")
63 
64 /* internal structures
65  */
66 DEF_OMOD_STATIC_DATA
67 
68 typedef struct _instanceData {
69 	enum { MD_SLEEP, MD_FAIL, MD_RANDFAIL, MD_ALWAYS_SUSPEND }
70 		mode;
71 	int	bEchoStdout;
72 	int	iWaitSeconds;
73 	int	iWaitUSeconds;	/* micro-seconds (one millionth of a second, just to make sure...) */
74 	int 	iCurrCallNbr;
75 	int	iFailFrequency;
76 	int	iResumeAfter;
77 	int	iCurrRetries;
78 	int	bFailed;	/* indicates if we are already in failed state - this is necessary
79 	 			 * to work properly together with multiple worker instances.
80 				 */
81 	pthread_mutex_t mut;
82 } instanceData;
83 
84 typedef struct wrkrInstanceData {
85 	instanceData *pData;
86 } wrkrInstanceData_t;
87 
88 typedef struct configSettings_s {
89 	int bEchoStdout;	/* echo non-failed messages to stdout */
90 } configSettings_t;
91 static configSettings_t cs;
92 
93 BEGINinitConfVars		/* (re)set config variables to default values */
94 CODESTARTinitConfVars
95 	cs.bEchoStdout = 0;
96 ENDinitConfVars
97 
98 BEGINcreateInstance
99 CODESTARTcreateInstance
100 	pData->iWaitSeconds = 1;
101 	pData->iWaitUSeconds = 0;
102 	pthread_mutex_init(&pData->mut, NULL);
103 ENDcreateInstance
104 
105 
106 BEGINcreateWrkrInstance
107 CODESTARTcreateWrkrInstance
108 ENDcreateWrkrInstance
109 
110 
111 BEGINdbgPrintInstInfo
112 CODESTARTdbgPrintInstInfo
113 	dbgprintf("Action delays rule by %d second(s) and %d microsecond(s)\n",
114 		  pData->iWaitSeconds, pData->iWaitUSeconds);
115 	/* do nothing */
116 ENDdbgPrintInstInfo
117 
118 
119 BEGINisCompatibleWithFeature
120 CODESTARTisCompatibleWithFeature
121 	/* we are not compatible with repeated msg reduction feature, so do not allow it */
122 ENDisCompatibleWithFeature
123 
124 
125 /* implement "fail" command in retry processing */
doFailOnResume(instanceData * pData)126 static rsRetVal doFailOnResume(instanceData *pData)
127 {
128 	DEFiRet;
129 
130 	dbgprintf("fail retry curr %d, max %d\n", pData->iCurrRetries, pData->iResumeAfter);
131 	if(++pData->iCurrRetries == pData->iResumeAfter) {
132 		iRet = RS_RET_OK;
133 		pData->bFailed = 0;
134 	} else {
135 		iRet = RS_RET_SUSPENDED;
136 	}
137 
138 	RETiRet;
139 }
140 
141 
142 /* implement "fail" command */
doFail(instanceData * pData)143 static rsRetVal doFail(instanceData *pData)
144 {
145 	DEFiRet;
146 
147 	dbgprintf("fail curr %d, frequency %d, bFailed %d\n", pData->iCurrCallNbr,
148 		  pData->iFailFrequency, pData->bFailed);
149 	if(pData->bFailed) {
150 		ABORT_FINALIZE(RS_RET_SUSPENDED);
151 	} else {
152 		if(pData->iCurrCallNbr++ % pData->iFailFrequency == 0) {
153 			pData->iCurrRetries = 0;
154 			pData->bFailed = 1;
155 			iRet = RS_RET_SUSPENDED;
156 		}
157 	}
158 finalize_it:
159 	RETiRet;
160 }
161 
162 
163 /* implement "sleep" command */
doSleep(instanceData * pData)164 static rsRetVal doSleep(instanceData *pData)
165 {
166 	DEFiRet;
167 	struct timeval tvSelectTimeout;
168 
169 	dbgprintf("sleep(%d, %d)\n", pData->iWaitSeconds, pData->iWaitUSeconds);
170 	tvSelectTimeout.tv_sec = pData->iWaitSeconds;
171 	tvSelectTimeout.tv_usec = pData->iWaitUSeconds; /* microseconds */
172 	select(0, NULL, NULL, NULL, &tvSelectTimeout);
173 	RETiRet;
174 }
175 
176 
177 /* implement "randomfail" command */
doRandFail(void)178 static rsRetVal doRandFail(void)
179 {
180 	DEFiRet;
181 	if((randomNumber() >> 4) < (RAND_MAX >> 5)) { /* rougly same probability */
182 		iRet = RS_RET_OK;
183 		dbgprintf("omtesting randfail: succeeded this time\n");
184 	} else {
185 		iRet = RS_RET_SUSPENDED;
186 		dbgprintf("omtesting randfail: failed this time\n");
187 	}
188 	RETiRet;
189 }
190 
191 
192 BEGINtryResume
193 CODESTARTtryResume
194 	dbgprintf("omtesting tryResume() called\n");
195 	pthread_mutex_lock(&pWrkrData->pData->mut);
196 	switch(pWrkrData->pData->mode) {
197 		case MD_SLEEP:
198 			break;
199 		case MD_FAIL:
200 			iRet = doFailOnResume(pWrkrData->pData);
201 			break;
202 		case MD_RANDFAIL:
203 			iRet = doRandFail();
204 			break;
205 		case MD_ALWAYS_SUSPEND:
206 			iRet = RS_RET_SUSPENDED;
207 	}
208 	pthread_mutex_unlock(&pWrkrData->pData->mut);
209 	dbgprintf("omtesting tryResume() returns iRet %d\n", iRet);
210 ENDtryResume
211 
212 
213 BEGINdoAction
214 	instanceData *pData;
215 CODESTARTdoAction
216 	dbgprintf("omtesting received msg '%s'\n", ppString[0]);
217 	pData = pWrkrData->pData;
218 	pthread_mutex_lock(&pData->mut);
219 	switch(pData->mode) {
220 		case MD_SLEEP:
221 			iRet = doSleep(pData);
222 			break;
223 		case MD_FAIL:
224 			iRet = doFail(pData);
225 			break;
226 		case MD_RANDFAIL:
227 			iRet = doRandFail();
228 			break;
229 		case MD_ALWAYS_SUSPEND:
230 			iRet = RS_RET_SUSPENDED;
231 			break;
232 	}
233 
234 	if(iRet == RS_RET_OK && pData->bEchoStdout) {
235 		fprintf(stdout, "%s", ppString[0]);
236 		fflush(stdout);
237 	}
238 	pthread_mutex_unlock(&pData->mut);
239 	dbgprintf(":omtesting: end doAction(), iRet %d\n", iRet);
240 ENDdoAction
241 
242 
243 BEGINfreeInstance
244 CODESTARTfreeInstance
245 	pthread_mutex_destroy(&pData->mut);
246 ENDfreeInstance
247 
248 
249 BEGINfreeWrkrInstance
250 CODESTARTfreeWrkrInstance
251 ENDfreeWrkrInstance
252 
253 
254 BEGINparseSelectorAct
255 	int i;
256 	uchar szBuf[1024];
257 CODESTARTparseSelectorAct
258 CODE_STD_STRING_REQUESTparseSelectorAct(1)
259 	/* code here is quick and dirty - if you like, clean it up. But keep
260 	 * in mind it is just a testing aid ;) -- rgerhards, 2007-12-31
261 	 */
262 	if(!strncmp((char*) p, ":omtesting:", sizeof(":omtesting:") - 1)) {
263 		p += sizeof(":omtesting:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
264 	} else {
265 		ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
266 	}
267 
268 	/* ok, if we reach this point, we have something for us */
269 	if((iRet = createInstance(&pData)) != RS_RET_OK)
270 		goto finalize_it;
271 
272 	/* check mode */
273 	for(i = 0 ; *p && !isspace((char) *p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
274 		szBuf[i] = (uchar) *p++;
275 	}
276 	szBuf[i] = '\0';
277 	if(isspace(*p))
278 		++p;
279 
280 	dbgprintf("omtesting command: '%s'\n", szBuf);
281 	if(!strcmp((char*) szBuf, "sleep")) {
282 		/* parse seconds */
283 		for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
284 			szBuf[i] = *p++;
285 		}
286 		szBuf[i] = '\0';
287 		if(isspace(*p))
288 			++p;
289 		pData->iWaitSeconds = atoi((char*) szBuf);
290 		/* parse microseconds */
291 		for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
292 			szBuf[i] = *p++;
293 		}
294 		szBuf[i] = '\0';
295 		if(isspace(*p))
296 			++p;
297 		pData->iWaitUSeconds = atoi((char*) szBuf);
298 		pData->mode = MD_SLEEP;
299 	} else if(!strcmp((char*) szBuf, "fail")) {
300 		/* "fail fail-freqency resume-after"
301 		 * fail-frequency specifies how often doAction() fails
302 		 * resume-after speicifes how fast tryResume() should come back with success
303 		 * all numbers being "times called"
304 		 */
305 		/* parse fail-frequence */
306 		for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
307 			szBuf[i] = *p++;
308 		}
309 		szBuf[i] = '\0';
310 		if(isspace(*p))
311 			++p;
312 		pData->iFailFrequency = atoi((char*) szBuf);
313 		/* parse resume-after */
314 		for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
315 			szBuf[i] = *p++;
316 		}
317 		szBuf[i] = '\0';
318 		if(isspace(*p))
319 			++p;
320 		pData->iResumeAfter = atoi((char*) szBuf);
321 		pData->iCurrCallNbr = 1;
322 		pData->mode = MD_FAIL;
323 	} else if(!strcmp((char*) szBuf, "randfail")) {
324 		pData->mode = MD_RANDFAIL;
325 	} else if(!strcmp((char*) szBuf, "always_suspend")) {
326 		pData->mode = MD_ALWAYS_SUSPEND;
327 	} else {
328 		dbgprintf("invalid mode '%s', doing 'sleep 1 0' - fix your config\n", szBuf);
329 	}
330 
331 	pData->bEchoStdout = cs.bEchoStdout;
332 	CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
333 				         (uchar*)"RSYSLOG_TraditionalForwardFormat"));
334 
335 CODE_STD_FINALIZERparseSelectorAct
336 ENDparseSelectorAct
337 
338 
339 BEGINmodExit
340 CODESTARTmodExit
341 ENDmodExit
342 
343 
344 BEGINqueryEtryPt
345 CODESTARTqueryEtryPt
346 CODEqueryEtryPt_STD_OMOD_QUERIES
347 CODEqueryEtryPt_STD_OMOD8_QUERIES
348 CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES
349 ENDqueryEtryPt
350 
351 
352 BEGINmodInit()
353 CODESTARTmodInit
354 INITLegCnfVars
355 	*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
356 CODEmodInit_QueryRegCFSLineHdlr
357 	CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomtestingechostdout", 0, eCmdHdlrBinary, NULL,
358 				   &cs.bEchoStdout, STD_LOADABLE_MODULE_ID));
359 	/* we seed the random-number generator in any case... */
360 	srand(time(NULL));
361 ENDmodInit
362 /*
363  * vi:set ai:
364  */
365