1 /* omuxsock.c
2  * This is the implementation of datgram unix domain socket forwarding.
3  *
4  * NOTE: read comments in module-template.h to understand how this file
5  *       works!
6  *
7  * Copyright 2010-2016 Adiscon GmbH.
8  *
9  * This file is part of rsyslog.
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  *       http://www.apache.org/licenses/LICENSE-2.0
16  *       -or-
17  *       see COPYING.ASL20 in the source distribution
18  *
19  * Unless required by applicable law or agreed to in writing, software
20  * distributed under the License is distributed on an "AS IS" BASIS,
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22  * See the License for the specific language governing permissions and
23  * limitations under the License.
24  */
25 #include "config.h"
26 #include "rsyslog.h"
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include "conf.h"
38 #include "srUtils.h"
39 #include "template.h"
40 #include "msg.h"
41 #include "cfsysline.h"
42 #include "module-template.h"
43 #include "glbl.h"
44 #include "errmsg.h"
45 #include "unicode-helper.h"
46 
47 MODULE_TYPE_OUTPUT
48 MODULE_TYPE_NOKEEP
49 MODULE_CNFNAME("omuxsock")
50 
51 /* internal structures
52  */
53 DEF_OMOD_STATIC_DATA
54 DEFobjCurrIf(glbl)
55 
56 #define INVLD_SOCK -1
57 
58 typedef struct _instanceData {
59 	permittedPeers_t *pPermPeers;
60 	uchar *sockName;
61 	int sock;
62 	struct sockaddr_un addr;
63 } instanceData;
64 
65 
66 typedef struct wrkrInstanceData {
67 	instanceData *pData;
68 } wrkrInstanceData_t;
69 
70 /* config data */
71 typedef struct configSettings_s {
72 	uchar *tplName; /* name of the default template to use */
73 	uchar *sockName; /* name of the default template to use */
74 } configSettings_t;
75 static configSettings_t cs;
76 
77 /* module-global parameters */
78 static struct cnfparamdescr modpdescr[] = {
79 	{ "template", eCmdHdlrGetWord, 0 },
80 };
81 static struct cnfparamblk modpblk =
82 	{ CNFPARAMBLK_VERSION,
83 	  sizeof(modpdescr)/sizeof(struct cnfparamdescr),
84 	  modpdescr
85 	};
86 
87 struct modConfData_s {
88 	rsconf_t *pConf;	/* our overall config object */
89 	uchar 	*tplName;	/* default template */
90 };
91 
92 static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
93 static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
94 
95 
96 static pthread_mutex_t mutDoAct = PTHREAD_MUTEX_INITIALIZER;
97 
98 BEGINinitConfVars		/* (re)set config variables to default values */
99 CODESTARTinitConfVars
100 	cs.tplName = NULL;
101 	cs.sockName = NULL;
102 ENDinitConfVars
103 
104 
105 static rsRetVal doTryResume(instanceData *pData);
106 
107 
108 /* this function gets the default template. It coordinates action between
109  * old-style and new-style configuration parts.
110  */
111 static uchar*
getDfltTpl(void)112 getDfltTpl(void)
113 {
114 	if(loadModConf != NULL && loadModConf->tplName != NULL)
115 		return loadModConf->tplName;
116 	else if(cs.tplName == NULL)
117 		return (uchar*)"RSYSLOG_TraditionalForwardFormat";
118 	else
119 		return cs.tplName;
120 }
121 
122 /* set the default template to be used
123  * This is a module-global parameter, and as such needs special handling. It needs to
124  * be coordinated with values set via the v2 config system (rsyslog v6+). What we do
125  * is we do not permit this directive after the v2 config system has been used to set
126  * the parameter.
127  */
128 static rsRetVal
setLegacyDfltTpl(void * pVal,uchar * newVal)129 setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal)
130 {
131 	DEFiRet;
132 
133 	if(loadModConf != NULL && loadModConf->tplName != NULL) {
134 		free(newVal);
135 		LogError(0, RS_RET_ERR, "omuxsock default template already set via module "
136 			"global parameter - can no longer be changed");
137 		ABORT_FINALIZE(RS_RET_ERR);
138 	}
139 	free(cs.tplName);
140 	cs.tplName = newVal;
141 finalize_it:
142 	RETiRet;
143 }
144 
145 
146 static rsRetVal
closeSocket(instanceData * pData)147 closeSocket(instanceData *pData)
148 {
149 	DEFiRet;
150 	if(pData->sock != INVLD_SOCK) {
151 		close(pData->sock);
152 		pData->sock = INVLD_SOCK;
153 	}
154 	RETiRet;
155 }
156 
157 
158 
159 
160 BEGINbeginCnfLoad
161 CODESTARTbeginCnfLoad
162 	loadModConf = pModConf;
163 	pModConf->pConf = pConf;
164 	pModConf->tplName = NULL;
165 ENDbeginCnfLoad
166 
167 BEGINsetModCnf
168 	struct cnfparamvals *pvals = NULL;
169 	int i;
170 CODESTARTsetModCnf
171 	pvals = nvlstGetParams(lst, &modpblk, NULL);
172 	if(pvals == NULL) {
173 		LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
174 				"config parameters [module(...)]");
175 		ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
176 	}
177 
178 	if(Debug) {
179 		dbgprintf("module (global) param blk for omuxsock:\n");
180 		cnfparamsPrint(&modpblk, pvals);
181 	}
182 
183 	for(i = 0 ; i < modpblk.nParams ; ++i) {
184 		if(!pvals[i].bUsed)
185 			continue;
186 		if(!strcmp(modpblk.descr[i].name, "template")) {
187 			loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
188 			if(cs.tplName != NULL) {
189 				LogError(0, RS_RET_DUP_PARAM, "omuxsock: default template "
190 						"was already set via legacy directive - may lead to inconsistent "
191 						"results.");
192 			}
193 		} else {
194 			dbgprintf("omuxsock: program error, non-handled "
195 			  "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
196 		}
197 	}
198 finalize_it:
199 	if(pvals != NULL)
200 		cnfparamvalsDestruct(pvals, &modpblk);
201 ENDsetModCnf
202 
203 BEGINendCnfLoad
204 CODESTARTendCnfLoad
205 	loadModConf = NULL; /* done loading */
206 	/* free legacy config vars */
207 	free(cs.tplName);
208 	cs.tplName = NULL;
209 ENDendCnfLoad
210 
211 BEGINcheckCnf
212 CODESTARTcheckCnf
213 ENDcheckCnf
214 
215 BEGINactivateCnf
216 CODESTARTactivateCnf
217 	runModConf = pModConf;
218 ENDactivateCnf
219 
220 BEGINfreeCnf
221 CODESTARTfreeCnf
222 	free(pModConf->tplName);
223 ENDfreeCnf
224 
225 BEGINcreateInstance
226 CODESTARTcreateInstance
227 	pData->sock = INVLD_SOCK;
228 ENDcreateInstance
229 
230 BEGINcreateWrkrInstance
231 CODESTARTcreateWrkrInstance
232 ENDcreateWrkrInstance
233 
234 
235 BEGINisCompatibleWithFeature
236 CODESTARTisCompatibleWithFeature
237 	if(eFeat == sFEATURERepeatedMsgReduction)
238 		iRet = RS_RET_OK;
239 ENDisCompatibleWithFeature
240 
241 
242 BEGINfreeInstance
243 CODESTARTfreeInstance
244 	/* final cleanup */
245 	closeSocket(pData);
246 	free(pData->sockName);
247 ENDfreeInstance
248 
249 BEGINfreeWrkrInstance
250 CODESTARTfreeWrkrInstance
251 ENDfreeWrkrInstance
252 
253 
254 BEGINdbgPrintInstInfo
255 CODESTARTdbgPrintInstInfo
256 	DBGPRINTF("%s", pData->sockName);
257 ENDdbgPrintInstInfo
258 
259 
260 /* Send a message via UDP
261  * rgehards, 2007-12-20
262  */
sendMsg(instanceData * pData,char * msg,size_t len)263 static rsRetVal sendMsg(instanceData *pData, char *msg, size_t len)
264 {
265 	DEFiRet;
266 	unsigned lenSent = 0;
267 
268 	if(pData->sock == INVLD_SOCK) {
269 		CHKiRet(doTryResume(pData));
270 	}
271 
272 	if(pData->sock != INVLD_SOCK) {
273 		lenSent = sendto(pData->sock, msg, len, 0, (const struct sockaddr *)&pData->addr,
274 			sizeof(pData->addr));
275 		if(lenSent != len) {
276 			int eno = errno;
277 			char errStr[1024];
278 			DBGPRINTF("omuxsock suspending: sendto(), socket %d, error: %d = %s.\n",
279 				pData->sock, eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
280 		}
281 	}
282 
283 finalize_it:
284 	RETiRet;
285 }
286 
287 
288 /* open socket to remote system
289  */
290 static rsRetVal
openSocket(instanceData * pData)291 openSocket(instanceData *pData)
292 {
293 	DEFiRet;
294 	assert(pData->sock == INVLD_SOCK);
295 
296 	if((pData->sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
297 		char errStr[1024];
298 		int eno = errno;
299 		DBGPRINTF("error %d creating AF_UNIX/SOCK_DGRAM: %s.\n",
300 			eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
301 		pData->sock = INVLD_SOCK;
302 		ABORT_FINALIZE(RS_RET_NO_SOCKET);
303 
304 	}
305 
306 	/* set up server address structure */
307 	memset(&pData->addr, 0, sizeof(pData->addr));
308 	pData->addr.sun_family = AF_UNIX;
309 	strncpy(pData->addr.sun_path, (char*)pData->sockName, sizeof(pData->addr.sun_path));
310 	pData->addr.sun_path[sizeof(pData->addr.sun_path)-1] = '\0';
311 
312 finalize_it:
313 	if(iRet != RS_RET_OK) {
314 		closeSocket(pData);
315 	}
316 	RETiRet;
317 }
318 
319 
320 
321 /* try to resume connection if it is not ready
322  */
doTryResume(instanceData * pData)323 static rsRetVal doTryResume(instanceData *pData)
324 {
325 	DEFiRet;
326 
327 	DBGPRINTF("omuxsock trying to resume\n");
328 	closeSocket(pData);
329 	iRet = openSocket(pData);
330 
331 	if(iRet != RS_RET_OK) {
332 		iRet = RS_RET_SUSPENDED;
333 	}
334 
335 	RETiRet;
336 }
337 
338 
339 BEGINtryResume
340 CODESTARTtryResume
341 	iRet = doTryResume(pWrkrData->pData);
342 ENDtryResume
343 
344 BEGINdoAction
345 	char *psz = NULL; /* temporary buffering */
346 	register unsigned l;
347 	int iMaxLine;
348 CODESTARTdoAction
349 	pthread_mutex_lock(&mutDoAct);
350 	CHKiRet(doTryResume(pWrkrData->pData));
351 
352 	iMaxLine = glbl.GetMaxLine();
353 
354 	DBGPRINTF(" omuxsock:%s\n", pWrkrData->pData->sockName);
355 
356 	psz = (char*) ppString[0];
357 	l = strlen((char*) psz);
358 	if((int) l > iMaxLine)
359 		l = iMaxLine;
360 
361 	CHKiRet(sendMsg(pWrkrData->pData, psz, l));
362 
363 finalize_it:
364 	pthread_mutex_unlock(&mutDoAct);
365 ENDdoAction
366 
367 
368 BEGINparseSelectorAct
369 CODESTARTparseSelectorAct
370 CODE_STD_STRING_REQUESTparseSelectorAct(1)
371 
372 	/* first check if this config line is actually for us */
373 	if(strncmp((char*) p, ":omuxsock:", sizeof(":omuxsock:") - 1)) {
374 		ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
375 	}
376 
377 	/* ok, if we reach this point, we have something for us */
378 	p += sizeof(":omuxsock:") - 1; /* eat indicator sequence  (-1 because of '\0'!) */
379 	CHKiRet(createInstance(&pData));
380 
381 	/* check if a non-standard template is to be applied */
382 	if(*(p-1) == ';')
383 		--p;
384 	CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, getDfltTpl()));
385 
386 	if(cs.sockName == NULL) {
387 		LogError(0, RS_RET_NO_SOCK_CONFIGURED, "No output socket configured for omuxsock\n");
388 		ABORT_FINALIZE(RS_RET_NO_SOCK_CONFIGURED);
389 	}
390 
391 	pData->sockName = cs.sockName;
392 	cs.sockName = NULL; /* pData is now owner and will fee it */
393 
394 CODE_STD_FINALIZERparseSelectorAct
395 ENDparseSelectorAct
396 
397 
398 /* a common function to free our configuration variables - used both on exit
399  * and on $ResetConfig processing. -- rgerhards, 2008-05-16
400  */
401 static void
freeConfigVars(void)402 freeConfigVars(void)
403 {
404 	free(cs.tplName);
405 	cs.tplName = NULL;
406 	free(cs.sockName);
407 	cs.sockName = NULL;
408 }
409 
410 
411 BEGINmodExit
412 CODESTARTmodExit
413 	/* release what we no longer need */
414 	objRelease(glbl, CORE_COMPONENT);
415 
416 	freeConfigVars();
417 ENDmodExit
418 
419 
420 BEGINqueryEtryPt
421 CODESTARTqueryEtryPt
422 CODEqueryEtryPt_STD_OMOD_QUERIES
423 CODEqueryEtryPt_STD_OMOD8_QUERIES
424 CODEqueryEtryPt_STD_CONF2_QUERIES
425 CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
426 ENDqueryEtryPt
427 
428 
429 /* Reset config variables for this module to default values.
430  * rgerhards, 2008-03-28
431  */
resetConfigVariables(uchar * pp,void * pVal)432 static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
433 {
434 	freeConfigVars();
435 	return RS_RET_OK;
436 }
437 
438 
439 BEGINmodInit()
440 CODESTARTmodInit
441 INITLegCnfVars
442 	*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
443 CODEmodInit_QueryRegCFSLineHdlr
444 	CHKiRet(objUse(glbl, CORE_COMPONENT));
445 
446 	CHKiRet(regCfSysLineHdlr((uchar *)"omuxsockdefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl,
447 		NULL, NULL));
448 	CHKiRet(regCfSysLineHdlr((uchar *)"omuxsocksocket", 0, eCmdHdlrGetWord, NULL, &cs.sockName, NULL));
449 	CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables,
450 	NULL, STD_LOADABLE_MODULE_ID));
451 ENDmodInit
452 
453 /* vim:set ai:
454  */
455