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