1 /* nsdpoll_ptcp.c
2  *
3  * An implementation of the nsd epoll() interface for plain tcp sockets.
4  *
5  * Copyright 2009-2016 Rainer Gerhards and Adiscon GmbH.
6  *
7  * This file is part of the rsyslog runtime library.
8  *
9  * The rsyslog runtime library is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * The rsyslog runtime library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with the rsyslog runtime library.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * A copy of the GPL can be found in the file "COPYING" in this distribution.
23  * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
24  */
25 #include "config.h"
26 
27 #ifdef HAVE_EPOLL_CREATE /* this module requires epoll! */
28 
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <string.h>
33 #ifdef HAVE_SYS_EPOLL_H
34 #	include <sys/epoll.h>
35 #endif
36 
37 #include "rsyslog.h"
38 #include "module-template.h"
39 #include "obj.h"
40 #include "errmsg.h"
41 #include "srUtils.h"
42 #include "nspoll.h"
43 #include "nsd_ptcp.h"
44 #include "nsdpoll_ptcp.h"
45 
46 /* static data */
47 DEFobjStaticHelpers
DEFobjCurrIf(glbl)48 DEFobjCurrIf(glbl)
49 
50 
51 /* -START------------------------- helpers for event list ------------------------------------ */
52 
53 /* add new entry to list. We assume that the fd is not already present and DO NOT check this!
54  * Returns newly created entry in pEvtLst.
55  * Note that we currently need to use level-triggered mode, because the upper layers do not work
56  * in parallel. As such, in edge-triggered mode we may not get notified, because new data comes
57  * in after we have read everything that was present. To use ET mode, we need to change the upper
58  * peers so that they immediately start a new wait before processing the data read. That obviously
59  * requires more elaborate redesign and we postpone this until the current more simplictic mode has
60  * been proven OK in practice.
61  * rgerhards, 2009-11-18
62  */
63 static rsRetVal
64 addEvent(nsdpoll_ptcp_t *pThis, int id, void *pUsr, int mode, nsd_ptcp_t *pSock, nsdpoll_epollevt_lst_t **pEvtLst)
65 {
66 	nsdpoll_epollevt_lst_t *pNew;
67 	DEFiRet;
68 
69 	CHKmalloc(pNew = (nsdpoll_epollevt_lst_t*) calloc(1, sizeof(nsdpoll_epollevt_lst_t)));
70 	pNew->id = id;
71 	pNew->pUsr = pUsr;
72 	pNew->pSock = pSock;
73 	pNew->event.events = 0; /* TODO: at some time we should be able to use EPOLLET */
74 	if(mode & NSDPOLL_IN)
75 		pNew->event.events |= EPOLLIN;
76 	if(mode & NSDPOLL_OUT)
77 		pNew->event.events |= EPOLLOUT;
78 	pNew->event.data.ptr = pNew;
79 	pthread_mutex_lock(&pThis->mutEvtLst);
80 	pNew->pNext = pThis->pRoot;
81 	pThis->pRoot = pNew;
82 	pthread_mutex_unlock(&pThis->mutEvtLst);
83 	*pEvtLst = pNew;
84 
85 finalize_it:
86 	RETiRet;
87 }
88 
89 
90 /* find and unlink the entry identified by id/pUsr from the list.
91  * rgerhards, 2009-11-23
92  */
93 static rsRetVal
unlinkEvent(nsdpoll_ptcp_t * pThis,int id,void * pUsr,nsdpoll_epollevt_lst_t ** ppEvtLst)94 unlinkEvent(nsdpoll_ptcp_t *pThis, int id, void *pUsr, nsdpoll_epollevt_lst_t **ppEvtLst)
95 {
96 	nsdpoll_epollevt_lst_t *pEvtLst;
97 	nsdpoll_epollevt_lst_t *pPrev = NULL;
98 	DEFiRet;
99 
100 	pthread_mutex_lock(&pThis->mutEvtLst);
101 	pEvtLst = pThis->pRoot;
102 	while(pEvtLst != NULL && !(pEvtLst->id == id && pEvtLst->pUsr == pUsr)) {
103 		pPrev = pEvtLst;
104 		pEvtLst = pEvtLst->pNext;
105 	}
106 	if(pEvtLst == NULL)
107 		ABORT_FINALIZE(RS_RET_NOT_FOUND);
108 
109 	*ppEvtLst = pEvtLst;
110 
111 	/* unlink */
112 	if(pPrev == NULL)
113 		pThis->pRoot = pEvtLst->pNext;
114 	else
115 		pPrev->pNext = pEvtLst->pNext;
116 
117 finalize_it:
118 	pthread_mutex_unlock(&pThis->mutEvtLst);
119 	RETiRet;
120 }
121 
122 
123 /* destruct the provided element. It must already be unlinked from the list.
124  * rgerhards, 2009-11-23
125  */
126 static rsRetVal
delEvent(nsdpoll_epollevt_lst_t ** ppEvtLst)127 delEvent(nsdpoll_epollevt_lst_t **ppEvtLst) {
128 	DEFiRet;
129 	free(*ppEvtLst);
130 	*ppEvtLst = NULL;
131 	RETiRet;
132 }
133 
134 
135 /* -END--------------------------- helpers for event list ------------------------------------ */
136 
137 
138 /* Standard-Constructor
139  */
140 BEGINobjConstruct(nsdpoll_ptcp) /* be sure to specify the object type also in END macro! */
141 #if defined(EPOLL_CLOEXEC) && defined(HAVE_EPOLL_CREATE1)
142 	DBGPRINTF("nsdpoll_ptcp uses epoll_create1()\n");
143 	pThis->efd = epoll_create1(EPOLL_CLOEXEC);
144 	if(pThis->efd < 0 && errno == ENOSYS)
145 #endif
146 	{
147 		DBGPRINTF("nsdpoll_ptcp uses epoll_create()\n");
148 		pThis->efd = epoll_create(100); /* size is ignored in newer kernels, but 100 is not bad... */
149 	}
150 
151 	if(pThis->efd < 0) {
152 		DBGPRINTF("epoll_create1() could not create fd\n");
153 		ABORT_FINALIZE(RS_RET_IO_ERROR);
154 	}
155 	pthread_mutex_init(&pThis->mutEvtLst, NULL);
156 finalize_it:
157 ENDobjConstruct(nsdpoll_ptcp)
158 
159 
160 /* destructor for the nsdpoll_ptcp object */
161 BEGINobjDestruct(nsdpoll_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */
162 	nsdpoll_epollevt_lst_t *node;
163 	nsdpoll_epollevt_lst_t *nextnode;
164 CODESTARTobjDestruct(nsdpoll_ptcp)
165 	/* we check if the epoll list still holds entries. This may happen, but
166 	 * is a bit unusual.
167 	 */
168 	if(pThis->pRoot != NULL) {
169 		for(node = pThis->pRoot ; node != NULL ; node = nextnode) {
170 			nextnode = node->pNext;
171 			dbgprintf("nsdpoll_ptcp destruct, need to destruct node %p\n", node);
172 			delEvent(&node);
173 		}
174 	}
175 	pthread_mutex_destroy(&pThis->mutEvtLst);
ENDobjDestruct(nsdpoll_ptcp)176 ENDobjDestruct(nsdpoll_ptcp)
177 
178 
179 /* Modify socket set */
180 static rsRetVal
181 Ctl(nsdpoll_t *pNsdpoll, nsd_t *pNsd, int id, void *pUsr, int mode, int op) {
182 	nsdpoll_ptcp_t *pThis = (nsdpoll_ptcp_t*) pNsdpoll;
183 	nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd;
184 	nsdpoll_epollevt_lst_t *pEventLst;
185 	int errSave;
186 	char errStr[512];
187 	DEFiRet;
188 
189 	if(op == NSDPOLL_ADD) {
190 		dbgprintf("adding nsdpoll entry %d/%p, sock %d\n", id, pUsr, pSock->sock);
191 		CHKiRet(addEvent(pThis, id, pUsr, mode, pSock, &pEventLst));
192 		if(epoll_ctl(pThis->efd, EPOLL_CTL_ADD,  pSock->sock, &pEventLst->event) < 0) {
193 			errSave = errno;
194 			rs_strerror_r(errSave, errStr, sizeof(errStr));
195 			LogError(errSave, RS_RET_ERR_EPOLL_CTL,
196 				"epoll_ctl failed on fd %d, id %d/%p, op %d with %s\n",
197 				pSock->sock, id, pUsr, mode, errStr);
198 		}
199 	} else if(op == NSDPOLL_DEL) {
200 		dbgprintf("removing nsdpoll entry %d/%p, sock %d\n", id, pUsr, pSock->sock);
201 		CHKiRet(unlinkEvent(pThis, id, pUsr, &pEventLst));
202 		if(epoll_ctl(pThis->efd, EPOLL_CTL_DEL, pSock->sock, &pEventLst->event) < 0) {
203 			errSave = errno;
204 			rs_strerror_r(errSave, errStr, sizeof(errStr));
205 			LogError(errSave, RS_RET_ERR_EPOLL_CTL,
206 				"epoll_ctl failed on fd %d, id %d/%p, op %d with %s\n",
207 				pSock->sock, id, pUsr, mode, errStr);
208 			ABORT_FINALIZE(RS_RET_ERR_EPOLL_CTL);
209 		}
210 		CHKiRet(delEvent(&pEventLst));
211 	} else {
212 		dbgprintf("program error: invalid NSDPOLL_mode %d - ignoring request\n", op);
213 		ABORT_FINALIZE(RS_RET_ERR);
214 	}
215 
216 finalize_it:
217 	RETiRet;
218 }
219 
220 
221 /* Wait for io to become ready. After the successful call, idRdy contains the
222  * id set by the caller for that i/o event, ppUsr is a pointer to a location
223  * where the user pointer shall be stored.
224  * numEntries contains the maximum number of entries on entry and the actual
225  * number of entries actually read on exit.
226  * rgerhards, 2009-11-18
227  */
228 static rsRetVal
Wait(nsdpoll_t * pNsdpoll,int timeout,int * numEntries,nsd_epworkset_t workset[])229 Wait(nsdpoll_t *pNsdpoll, int timeout, int *numEntries, nsd_epworkset_t workset[])
230 {
231 	nsdpoll_ptcp_t *pThis = (nsdpoll_ptcp_t*) pNsdpoll;
232 	nsdpoll_epollevt_lst_t *pOurEvt;
233 	struct epoll_event event[128];
234 	int nfds;
235 	int i;
236 	DEFiRet;
237 
238 	assert(workset != NULL);
239 
240 	if(*numEntries > 128)
241 		*numEntries = 128;
242 	DBGPRINTF("doing epoll_wait for max %d events\n", *numEntries);
243 	nfds = epoll_wait(pThis->efd, event, *numEntries, timeout);
244 	if(nfds == -1) {
245 		if(errno == EINTR) {
246 			ABORT_FINALIZE(RS_RET_EINTR);
247 		} else {
248 			DBGPRINTF("epoll() returned with error code %d\n", errno);
249 			ABORT_FINALIZE(RS_RET_ERR_EPOLL);
250 		}
251 	} else if(nfds == 0) {
252 		ABORT_FINALIZE(RS_RET_TIMEOUT);
253 	}
254 
255 	/* we got valid events, so tell the caller... */
256 	DBGPRINTF("epoll returned %d entries\n", nfds);
257 	for(i = 0 ; i < nfds ; ++i) {
258 		pOurEvt = (nsdpoll_epollevt_lst_t*) event[i].data.ptr;
259 		workset[i].id = pOurEvt->id;
260 		workset[i].pUsr = pOurEvt->pUsr;
261 	}
262 	*numEntries = nfds;
263 
264 finalize_it:
265 	RETiRet;
266 }
267 
268 
269 /* ------------------------------ end support for the epoll() interface ------------------------------ */
270 
271 
272 /* queryInterface function */
273 BEGINobjQueryInterface(nsdpoll_ptcp)
274 CODESTARTobjQueryInterface(nsdpoll_ptcp)
275 	if(pIf->ifVersion != nsdCURR_IF_VERSION) {/* check for current version, increment on each change */
276 		ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
277 	}
278 
279 	/* ok, we have the right interface, so let's fill it
280 	 * Please note that we may also do some backwards-compatibility
281 	 * work here (if we can support an older interface version - that,
282 	 * of course, also affects the "if" above).
283 	 */
284 	pIf->Construct = (rsRetVal(*)(nsdpoll_t**)) nsdpoll_ptcpConstruct;
285 	pIf->Destruct = (rsRetVal(*)(nsdpoll_t**)) nsdpoll_ptcpDestruct;
286 	pIf->Ctl = Ctl;
287 	pIf->Wait = Wait;
288 finalize_it:
289 ENDobjQueryInterface(nsdpoll_ptcp)
290 
291 
292 /* exit our class
293  */
294 BEGINObjClassExit(nsdpoll_ptcp, OBJ_IS_CORE_MODULE) /* CHANGE class also in END MACRO! */
295 CODESTARTObjClassExit(nsdpoll_ptcp)
296 	/* release objects we no longer need */
297 	objRelease(glbl, CORE_COMPONENT);
298 ENDObjClassExit(nsdpoll_ptcp)
299 
300 
301 /* Initialize the nsdpoll_ptcp class. Must be called as the very first method
302  * before anything else is called inside this class.
303  * rgerhards, 2008-02-19
304  */
305 BEGINObjClassInit(nsdpoll_ptcp, 1, OBJ_IS_CORE_MODULE) /* class, version */
306 	/* request objects we use */
307 	CHKiRet(objUse(glbl, CORE_COMPONENT));
308 
309 	/* set our own handlers */
310 ENDObjClassInit(nsdpoll_ptcp)
311 #else
312 
313 #ifdef __xlc__ /* Xlc require some code, even unused, in source file*/
314 static void dummy(void) {}
315 #endif
316 
317 #endif /* #ifdef HAVE_EPOLL_CREATE this module requires epoll! */
318 
319 /* vi:set ai:
320  */
321