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