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