1 /* mmcount.c
2  * count messages by priority or json property of given app-name.
3  *
4  * Copyright 2013 Red Hat Inc.
5  * Copyright 2014 Rainer Gerhards
6  *
7  * This file is part of rsyslog.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *       http://www.apache.org/licenses/LICENSE-2.0
14  *       -or-
15  *       see COPYING.ASL20 in the source distribution
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 #include "config.h"
24 #include "rsyslog.h"
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <stdint.h>
34 #include <json.h>
35 #include "conf.h"
36 #include "syslogd-types.h"
37 #include "srUtils.h"
38 #include "template.h"
39 #include "module-template.h"
40 #include "errmsg.h"
41 #include "hashtable.h"
42 
43 
44 #define JSON_COUNT_NAME "!mmcount"
45 #define SEVERITY_COUNT 8
46 
47 MODULE_TYPE_OUTPUT
48 MODULE_TYPE_NOKEEP
49 MODULE_CNFNAME("mmcount")
50 
51 
52 DEF_OMOD_STATIC_DATA
53 
54 /* config variables */
55 
56 typedef struct _instanceData {
57 	char *pszAppName;
58 	int severity[SEVERITY_COUNT];
59 	char *pszKey;
60 	char *pszValue;
61 	int valueCounter;
62 	struct hashtable *ht;
63 	pthread_mutex_t mut;
64 } instanceData;
65 
66 typedef struct wrkrInstanceData {
67 	instanceData *pData;
68 } wrkrInstanceData_t;
69 
70 struct modConfData_s {
71 	rsconf_t *pConf;	/* our overall config object */
72 };
73 static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
74 static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
75 
76 
77 /* tables for interfacing with the v6 config system */
78 /* action (instance) parameters */
79 static struct cnfparamdescr actpdescr[] = {
80 	{ "appname", eCmdHdlrGetWord, 0 },
81 	{ "key", eCmdHdlrGetWord, 0 },
82 	{ "value", eCmdHdlrGetWord, 0 },
83 };
84 static struct cnfparamblk actpblk =
85 	{ CNFPARAMBLK_VERSION,
86 	  sizeof(actpdescr)/sizeof(struct cnfparamdescr),
87 	  actpdescr
88 	};
89 
90 BEGINbeginCnfLoad
91 CODESTARTbeginCnfLoad
92 	loadModConf = pModConf;
93 	pModConf->pConf = pConf;
94 ENDbeginCnfLoad
95 
96 BEGINendCnfLoad
97 CODESTARTendCnfLoad
98 ENDendCnfLoad
99 
100 BEGINcheckCnf
101 CODESTARTcheckCnf
102 ENDcheckCnf
103 
104 BEGINactivateCnf
105 CODESTARTactivateCnf
106 	runModConf = pModConf;
107 ENDactivateCnf
108 
109 BEGINfreeCnf
110 CODESTARTfreeCnf
111 ENDfreeCnf
112 
113 
114 BEGINcreateInstance
115 CODESTARTcreateInstance
116 	pthread_mutex_init(&pData->mut, NULL);
117 ENDcreateInstance
118 
119 BEGINcreateWrkrInstance
120 CODESTARTcreateWrkrInstance
121 ENDcreateWrkrInstance
122 
123 
124 BEGINisCompatibleWithFeature
125 CODESTARTisCompatibleWithFeature
126 ENDisCompatibleWithFeature
127 
128 
129 BEGINfreeInstance
130 CODESTARTfreeInstance
131 ENDfreeInstance
132 
133 
134 BEGINfreeWrkrInstance
135 CODESTARTfreeWrkrInstance
136 ENDfreeWrkrInstance
137 
138 static inline void
setInstParamDefaults(instanceData * pData)139 setInstParamDefaults(instanceData *pData)
140 {
141 	int i;
142 
143 	pData->pszAppName = NULL;
144 	for (i = 0; i < SEVERITY_COUNT; i++)
145 	        pData->severity[i] = 0;
146 	pData->pszKey = NULL;
147 	pData->pszValue = NULL;
148 	pData->valueCounter = 0;
149 	pData->ht = NULL;
150 }
151 
152 static unsigned int
hash_from_key_fn(void * k)153 hash_from_key_fn(void *k)
154 {
155 	return *(unsigned int *)k;
156 }
157 
158 static int
key_equals_fn(void * k1,void * k2)159 key_equals_fn(void *k1, void *k2)
160 {
161 	return (*(unsigned int *)k1 == *(unsigned int *)k2);
162 }
163 
164 BEGINnewActInst
165 	struct cnfparamvals *pvals;
166 	int i;
167 CODESTARTnewActInst
168 	DBGPRINTF("newActInst (mmcount)\n");
169 	if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
170 		ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
171 	}
172 
173 	CODE_STD_STRING_REQUESTnewActInst(1)
174 	CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
175 	CHKiRet(createInstance(&pData));
176 	setInstParamDefaults(pData);
177 
178 	for(i = 0 ; i < actpblk.nParams ; ++i) {
179 		if(!pvals[i].bUsed)
180 			continue;
181 		if(!strcmp(actpblk.descr[i].name, "appname")) {
182 			pData->pszAppName = es_str2cstr(pvals[i].val.d.estr, NULL);
183 			continue;
184 		}
185 		if(!strcmp(actpblk.descr[i].name, "key")) {
186 			pData->pszKey = es_str2cstr(pvals[i].val.d.estr, NULL);
187 			continue;
188 		}
189 		if(!strcmp(actpblk.descr[i].name, "value")) {
190 			pData->pszValue = es_str2cstr(pvals[i].val.d.estr, NULL);
191 			continue;
192 		}
193 		dbgprintf("mmcount: program error, non-handled "
194 			  "param '%s'\n", actpblk.descr[i].name);
195 	}
196 
197 	if(pData->pszAppName == NULL) {
198 		dbgprintf("mmcount: action requires a appname");
199 		ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
200 	}
201 
202 	if(pData->pszKey != NULL && pData->pszValue == NULL) {
203 		if(NULL == (pData->ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL))) {
204 			DBGPRINTF("mmcount: error creating hash table!\n");
205 			ABORT_FINALIZE(RS_RET_ERR);
206 		}
207 	}
208 CODE_STD_FINALIZERnewActInst
209 	cnfparamvalsDestruct(pvals, &actpblk);
210 ENDnewActInst
211 
212 
213 BEGINdbgPrintInstInfo
214 CODESTARTdbgPrintInstInfo
215 ENDdbgPrintInstInfo
216 
217 
218 BEGINtryResume
219 CODESTARTtryResume
220 ENDtryResume
221 
222 static int *
getCounter(struct hashtable * ht,const char * str)223 getCounter(struct hashtable *ht, const char *str) {
224 	unsigned int key;
225 	int *pCounter;
226 	unsigned int *pKey;
227 
228 	/* we dont store str as key, instead we store hash of the str
229 	   as key to reduce memory usage */
230 	key = hash_from_string((char*)str);
231 	pCounter = hashtable_search(ht, &key);
232 	if(pCounter) {
233 		return pCounter;
234 	}
235 
236 	/* counter is not found for the str, so add new entry and
237 	   return the counter */
238 	if(NULL == (pKey = (unsigned int*)malloc(sizeof(unsigned int)))) {
239 		DBGPRINTF("mmcount: memory allocation for key failed\n");
240 		return NULL;
241 	}
242 	*pKey = key;
243 
244 	if(NULL == (pCounter = (int*)malloc(sizeof(int)))) {
245 		DBGPRINTF("mmcount: memory allocation for value failed\n");
246 		free(pKey);
247 		return NULL;
248 	}
249 	*pCounter = 0;
250 
251 	if(!hashtable_insert(ht, pKey, pCounter)) {
252 		DBGPRINTF("mmcount: inserting element into hashtable failed\n");
253 		free(pKey);
254 		free(pCounter);
255 		return NULL;
256 	}
257 	return pCounter;
258 }
259 
260 BEGINdoAction_NoStrings
261 	smsg_t **ppMsg = (smsg_t **) pMsgData;
262 	smsg_t *pMsg = ppMsg[0];
263 	char *appname;
264 	struct json_object *json = NULL;
265 	struct json_object *keyjson = NULL;
266 	const char *pszValue;
267 	int *pCounter;
268 	instanceData *const pData = pWrkrData->pData;
269 CODESTARTdoAction
270 	appname = getAPPNAME(pMsg, LOCK_MUTEX);
271 
272 	pthread_mutex_lock(&pData->mut);
273 	if(0 != strcmp(appname, pData->pszAppName)) {
274 		/* we are not working for this appname. nothing to do */
275 		ABORT_FINALIZE(RS_RET_OK);
276 	}
277 
278 	if(!pData->pszKey) {
279 		/* no key given for count, so we count severity */
280 		if(pMsg->iSeverity < SEVERITY_COUNT) {
281 			pData->severity[pMsg->iSeverity]++;
282 			json = json_object_new_int(pData->severity[pMsg->iSeverity]);
283 		}
284 		ABORT_FINALIZE(RS_RET_OK);
285 	}
286 
287 	/* key is given, so get the property json */
288 	msgPropDescr_t pProp;
289 	msgPropDescrFill(&pProp, (uchar*)pData->pszKey, strlen(pData->pszKey));
290 	rsRetVal localRet = msgGetJSONPropJSON(pMsg, &pProp, &keyjson);
291 	msgPropDescrDestruct(&pProp);
292 	if(localRet != RS_RET_OK) {
293 		/* key not found in the message. nothing to do */
294 		ABORT_FINALIZE(RS_RET_OK);
295 	}
296 
297 	/* key found, so get the value */
298 	pszValue = (char*)json_object_get_string(keyjson);
299 	if(pszValue == NULL) { /* json null object returns NULL! */
300 		pszValue = "";
301 	}
302 
303 	if(pData->pszValue) {
304 		/* value also given for count */
305 		if(!strcmp(pszValue, pData->pszValue)) {
306 			/* count for (value and key and appname) matched */
307 			pData->valueCounter++;
308 			json = json_object_new_int(pData->valueCounter);
309 		}
310 		ABORT_FINALIZE(RS_RET_OK);
311 	}
312 
313 	/* value is not given, so we count for each value of given key */
314 	pCounter = getCounter(pData->ht, pszValue);
315 	if(pCounter) {
316 		(*pCounter)++;
317 		json = json_object_new_int(*pCounter);
318 	}
319 finalize_it:
320 	pthread_mutex_unlock(&pData->mut);
321 
322 	if(json) {
323 		msgAddJSON(pMsg, (uchar *)JSON_COUNT_NAME, json, 0, 0);
324 	}
325 ENDdoAction
326 
327 
328 NO_LEGACY_CONF_parseSelectorAct
329 
330 
331 BEGINmodExit
332 CODESTARTmodExit
333 ENDmodExit
334 
335 
336 BEGINqueryEtryPt
337 CODESTARTqueryEtryPt
338 CODEqueryEtryPt_STD_OMOD_QUERIES
339 CODEqueryEtryPt_STD_OMOD8_QUERIES
340 CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
341 CODEqueryEtryPt_STD_CONF2_QUERIES
342 ENDqueryEtryPt
343 
344 
345 
346 BEGINmodInit()
347 CODESTARTmodInit
348 	*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
349 CODEmodInit_QueryRegCFSLineHdlr
350 	DBGPRINTF("mmcount: module compiled with rsyslog version %s.\n", VERSION);
351 ENDmodInit
352