3 * Common code for plain TCP based servers. This is currently being
4 * utilized by imtcp and imgssapi. I suspect that when we implement
5 * SSL/TLS, that module could also use tcpsrv.
7 * There are actually two classes within the tcpserver code: one is
8 * the tcpsrv itself, the other one is its sessions. This is a helper
11 * The common code here calls upon specific functionality by using
12 * callbacks. The specialised input modules need to set the proper
13 * callbacks before the code is run. The tcpsrv then calls back
14 * into the specific input modules at the appropriate time.
16 * NOTE: read comments in module-template.h to understand how this file
19 * File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
21 * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
23 * This file is part of rsyslog.
25 * Rsyslog is free software: you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation, either version 3 of the License, or
28 * (at your option) any later version.
30 * Rsyslog is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
38 * A copy of the GPL can be found in the file "COPYING" in this distribution.
49 #include <netinet/in.h>
51 #include <sys/types.h>
52 #include <sys/socket.h>
58 #include "cfsysline.h"
59 #include "module-template.h"
70 #define TCPSESS_MAX_DEFAULT 200 /* default for nbr of tcp sessions if no number is given */
75 DEFobjCurrIf(tcps_sess)
81 /* code to free all sockets within a socket table.
82 * A socket table is a descriptor table where the zero
83 * element has the count of elements. This is used for
84 * listening sockets. The socket table itself is also
86 * A POINTER to this structure must be provided, thus
88 * rgerhards, 2007-06-28
90 static void freeAllSockets(int **socks)
92 assert(socks != NULL);
93 assert(*socks != NULL);
95 dbgprintf("Closing socket %d.\n", (*socks)[**socks]);
96 close((*socks)[**socks]);
104 /* configure TCP listener settings. This is called during command
105 * line parsing. The argument following -t is supplied as an argument.
106 * The format of this argument is
107 * "<port-to-use>, <nbr-of-sessions>"
108 * Typically, there is no whitespace between port and session number.
109 * (but it may be...).
110 * NOTE: you can not use dbgprintf() in here - the dbgprintf() system is
111 * not yet initilized when this function is called.
112 * rgerhards, 2007-06-21
113 * The port in cOptarg is handed over to us - the caller MUST NOT free it!
114 * rgerhards, 2008-03-20
117 configureTCPListen(tcpsrv_t *pThis, char *cOptarg)
121 register char *pNode = NULL;
123 assert(cOptarg != NULL);
124 ISOBJ_TYPE_assert(pThis, tcpsrv);
126 if(pThis->TCPLstnNode != NULL) {
127 free(pThis->TCPLstnNode);
128 pThis->TCPLstnNode = NULL;
131 pArg = strchr(cOptarg, ':');
134 pThis->TCPLstnNode = cOptarg;
135 cOptarg = strdup(pArg);
141 while(isdigit((int) *pArg)) {
142 i = i * 10 + *pArg++ - '0';
145 if(pThis->TCPLstnPort != NULL) {
146 free(pThis->TCPLstnPort);
147 pThis->TCPLstnPort = NULL;
150 if( i >= 0 && i <= 65535) {
151 pThis->TCPLstnPort = cOptarg;
154 errmsg.LogError(NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg);
159 #if 0 // I think this is no longer needed
161 configureTCPListenSessMax(char *cOptarg)
164 register char *pArg = cOptarg;
166 assert(cOptarg != NULL);
168 /* number of sessions */
170 while(isdigit((int) *pArg)) {
171 i = i * 10 + *pArg++ - '0';
177 /* too small, need to adjust */
178 errmsg.LogError(NO_ERRCODE, "TCP session max configured to %s - changing to 1.\n", cOptarg);
185 /* Initialize the session table
186 * returns 0 if OK, somewhat else otherwise
189 TCPSessTblInit(tcpsrv_t *pThis)
193 ISOBJ_TYPE_assert(pThis, tcpsrv);
194 assert(pThis->pSessions == NULL);
196 dbgprintf("Allocating buffer for %d TCP sessions.\n", pThis->iSessMax);
197 if((pThis->pSessions = (tcps_sess_t **) calloc(pThis->iSessMax, sizeof(tcps_sess_t *))) == NULL) {
198 dbgprintf("Error: TCPSessInit() could not alloc memory for TCP session table.\n");
199 ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
207 /* find a free spot in the session table. If the table
208 * is full, -1 is returned, else the index of the free
209 * entry (0 or higher).
212 TCPSessTblFindFreeSpot(tcpsrv_t *pThis)
216 ISOBJ_TYPE_assert(pThis, tcpsrv);
218 for(i = 0 ; i < pThis->iSessMax ; ++i) {
219 if(pThis->pSessions[i] == NULL)
223 return((i < pThis->iSessMax) ? i : -1);
227 /* Get the next session index. Free session tables entries are
228 * skipped. This function is provided the index of the last
229 * session entry, or -1 if no previous entry was obtained. It
230 * returns the index of the next session or -1, if there is no
231 * further entry in the table. Please note that the initial call
232 * might as well return -1, if there is no session at all in the
236 TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
240 ISOBJ_TYPE_assert(pThis, tcpsrv);
241 for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i)
242 if(pThis->pSessions[i] != NULL)
245 return((i < pThis->iSessMax) ? i : -1);
249 /* De-Initialize TCP listner sockets.
250 * This function deinitializes everything, including freeing the
251 * session table. No TCP listen receive operations are permitted
252 * unless the subsystem is reinitialized.
253 * rgerhards, 2007-06-21
255 static void deinit_tcp_listener(tcpsrv_t *pThis)
259 ISOBJ_TYPE_assert(pThis, tcpsrv);
260 assert(pThis->pSessions != NULL);
262 /* close all TCP connections! */
263 iTCPSess = TCPSessGetNxtSess(pThis, -1);
264 while(iTCPSess != -1) {
265 tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
266 /* now get next... */
267 iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
270 /* we are done with the session table - so get rid of it...
272 free(pThis->pSessions);
273 pThis->pSessions = NULL; /* just to make sure... */
275 if(pThis->TCPLstnNode != NULL)
276 free(pThis->TCPLstnNode);
277 if(pThis->TCPLstnPort != NULL)
278 free(pThis->TCPLstnPort);
280 /* finally close the listen sockets themselfs */
281 freeAllSockets(&pThis->pSocksLstn);
285 /* Initialize TCP sockets (for listener)
286 * This function returns either NULL (which means it failed) or
287 * a pointer to an array of file descriptiors. If the pointer is
288 * returned, the zeroest element [0] contains the count of valid
289 * descriptors. The descriptors themself follow in range
290 * [1] ... [num-descriptors]. It is guaranteed that each of these
291 * descriptors is valid, at least when this function returns.
292 * Please note that technically the array may be larger than the number
293 * of valid pointers stored in it. The memory overhead is minimal, so
294 * we do not bother to re-allocate an array of the exact size. Logically,
295 * the array still contains the exactly correct number of descriptors.
297 static int *create_tcp_socket(tcpsrv_t *pThis)
299 struct addrinfo hints, *res, *r;
300 int error, maxs, *s, *socks, on = 1;
303 ISOBJ_TYPE_assert(pThis, tcpsrv);
305 if(!strcmp(pThis->TCPLstnPort, "0"))
307 /* use default - we can not do service db update, because there is
308 * no IANA-assignment for syslog/tcp. In the long term, we might
309 * re-use RFC 3195 port of 601, but that would probably break to
310 * many existing configurations.
311 * rgerhards, 2007-06-28
314 TCPLstnPort = pThis->TCPLstnPort;
315 dbgprintf("creating tcp socket on port %s\n", TCPLstnPort);
316 memset(&hints, 0, sizeof(hints));
317 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
318 hints.ai_family = family;
319 hints.ai_socktype = SOCK_STREAM;
321 error = getaddrinfo(pThis->TCPLstnNode, TCPLstnPort, &hints, &res);
323 errmsg.LogError(NO_ERRCODE, "%s", gai_strerror(error));
327 /* Count max number of sockets we may open */
328 for (maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++)
330 socks = malloc((maxs+1) * sizeof(int));
332 errmsg.LogError(NO_ERRCODE, "couldn't allocate memory for TCP listen sockets, suspending TCP message reception.");
337 *socks = 0; /* num of sockets counter at start of array */
339 for (r = res; r != NULL ; r = r->ai_next) {
340 *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
342 if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT))
343 errmsg.LogError(NO_ERRCODE, "create_tcp_socket(), socket");
344 /* it is debatable if PF_INET with EAFNOSUPPORT should
351 if (r->ai_family == AF_INET6) {
353 if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
354 (char *)&iOn, sizeof (iOn)) < 0) {
355 errmsg.LogError(NO_ERRCODE, "TCP setsockopt");
362 if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR,
363 (char *) &on, sizeof(on)) < 0 ) {
364 errmsg.LogError(NO_ERRCODE, "TCP setsockopt(REUSEADDR)");
370 /* We need to enable BSD compatibility. Otherwise an attacker
371 * could flood our log files by sending us tons of ICMP errors.
374 if(net.should_use_so_bsdcompat()) {
375 if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT,
376 (char *) &on, sizeof(on)) < 0) {
377 errmsg.LogError(NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
385 if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0)
387 && (errno != EADDRINUSE)
390 errmsg.LogError(NO_ERRCODE, "TCP bind");
396 if( listen(*s,pThis->iSessMax / 10 + 5) < 0) {
397 /* If the listen fails, it most probably fails because we ask
398 * for a too-large backlog. So in this case we first set back
399 * to a fixed, reasonable, limit that should work. Only if
400 * that fails, too, we give up.
402 errmsg.LogError(NO_ERRCODE, "listen with a backlog of %d failed - retrying with default of 32.",
403 pThis->iSessMax / 10 + 5);
404 if(listen(*s, 32) < 0) {
405 errmsg.LogError(NO_ERRCODE, "TCP listen, suspending tcp inet");
419 if(Debug && *socks != maxs)
420 dbgprintf("We could initialize %d TCP listen sockets out of %d we received "
421 "- this may or may not be an error indication.\n", *socks, maxs);
424 errmsg.LogError(NO_ERRCODE, "No TCP listen socket could successfully be initialized, "
425 "message reception via TCP disabled.\n");
430 /* OK, we had success. Now it is also time to
431 * initialize our connections
433 if(TCPSessTblInit(pThis) != 0) {
434 /* OK, we are in some trouble - we could not initialize the
435 * session table, so we can not continue. We need to free all
436 * we have assigned so far, because we can not really use it...
438 errmsg.LogError(NO_ERRCODE, "Could not initialize TCP session table, suspending TCP message reception.");
439 freeAllSockets(&socks); /* prevent a socket leak */
447 /* Accept new TCP connection; make entry in session table. If there
448 * is no more space left in the connection table, the new TCP
449 * connection is immediately dropped.
450 * ppSess has a pointer to the newly created session, if it succeds.
451 * If it does not succeed, no session is created and ppSess is
452 * undefined. If the user has provided an OnSessAccept Callback,
453 * this one is executed immediately after creation of the
454 * session object, so that it can do its own initialization.
455 * rgerhards, 2008-03-02
458 SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, int fd)
464 struct sockaddr_storage addr;
465 socklen_t addrlen = sizeof(struct sockaddr_storage);
466 uchar fromHost[NI_MAXHOST];
467 uchar fromHostFQDN[NI_MAXHOST];
469 ISOBJ_TYPE_assert(pThis, tcpsrv);
471 newConn = accept(fd, (struct sockaddr*) &addr, &addrlen);
473 errmsg.LogError(NO_ERRCODE, "tcp accept, ignoring error and connection request");
474 ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
478 /* Add to session list */
479 iSess = TCPSessTblFindFreeSpot(pThis);
482 errmsg.LogError(NO_ERRCODE, "too many tcp sessions - dropping incoming request");
484 ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
487 /* we found a free spot and can construct our session object */
488 CHKiRet(tcps_sess.Construct(&pSess));
489 CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
492 /* OK, we have a "good" index... */
493 /* get the host name */
494 if(net.cvthname(&addr, fromHost, fromHostFQDN) != RS_RET_OK) {
495 /* we seem to have something malicous - at least we
496 * are now told to discard the connection request.
497 * Error message has been generated by cvthname.
500 ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
504 /* Here we check if a host is permitted to send us
505 * syslog messages. If it isn't, we do not further
506 * process the message but log a warning (if we are
507 * configured to do this).
508 * rgerhards, 2005-09-26
510 RUNLOG_VAR("%p", ppSess);
511 RUNLOG_VAR("%p", pSess);
512 if(!pThis->pIsPermittedHost((struct sockaddr*) &addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) {
513 dbgprintf("%s is not an allowed sender\n", (char *) fromHostFQDN);
514 if(option_DisallowWarning) {
516 errmsg.LogError(NO_ERRCODE, "TCP message from disallowed sender %s discarded",
520 ABORT_FINALIZE(RS_RET_HOST_NOT_PERMITTED);
523 /* OK, we have an allowed sender, so let's continue, what
524 * means we can finally fill in the session object.
526 CHKiRet(tcps_sess.SetHost(pSess, fromHost));
527 CHKiRet(tcps_sess.SetSock(pSess, newConn));
528 CHKiRet(tcps_sess.SetMsgIdx(pSess, 0));
529 CHKiRet(tcps_sess.ConstructFinalize(pSess));
531 /* check if we need to call our callback */
532 if(pThis->pOnSessAccept != NULL) {
533 CHKiRet(pThis->pOnSessAccept(pThis, pSess));
537 pThis->pSessions[iSess] = pSess;
540 if(iRet != RS_RET_OK) {
542 if(pThis->pSessions[iSess] != NULL)
543 tcps_sess.Destruct(&pThis->pSessions[iSess]);
545 iSess = -1; // TODO: change this to be fully iRet compliant ;)
552 /* This function is called to gather input.
563 tcps_sess_t *pNewSess;
565 ISOBJ_TYPE_assert(pThis, tcpsrv);
567 /* this is an endless loop - it is terminated when the thread is
568 * signalled to do so. This, however, is handled by the framework,
569 * right into the sleep below.
575 /* Add the TCP listen sockets to the list of read descriptors.
577 if(pThis->pSocksLstn != NULL && *pThis->pSocksLstn) {
578 for (i = 0; i < *pThis->pSocksLstn; i++) {
579 /* The if() below is theoretically not needed, but I leave it in
580 * so that a socket may become unsuable during execution. That
581 * feature is not yet supported by the current code base.
583 if (pThis->pSocksLstn[i+1] != -1) {
585 net.debugListenInfo(pThis->pSocksLstn[i+1], "TCP");
586 FD_SET(pThis->pSocksLstn[i+1], &readfds);
587 if(pThis->pSocksLstn[i+1]>maxfds) maxfds=pThis->pSocksLstn[i+1];
590 /* do the sessions */
591 iTCPSess = TCPSessGetNxtSess(pThis, -1);
592 while(iTCPSess != -1) {
594 fdSess = pThis->pSessions[iTCPSess]->sock; // TODO: NOT CLEAN!, use method
595 dbgprintf("Adding TCP Session %d\n", fdSess);
596 FD_SET(fdSess, &readfds);
597 if (fdSess>maxfds) maxfds=fdSess;
598 /* now get next... */
599 iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
604 // TODO: name in dbgprintf!
605 dbgprintf("--------<TCPSRV> calling select, active file descriptors (max %d): ", maxfds);
606 for (nfds = 0; nfds <= maxfds; ++nfds)
607 if ( FD_ISSET(nfds, &readfds) )
608 dbgprintf("%d ", nfds);
612 /* wait for io to become ready */
613 nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
615 for (i = 0; i < *pThis->pSocksLstn; i++) {
616 if (FD_ISSET(pThis->pSocksLstn[i+1], &readfds)) {
617 dbgprintf("New connect on TCP inetd socket: #%d\n", pThis->pSocksLstn[i+1]);
618 RUNLOG_VAR("%p", &pNewSess);
619 SessAccept(pThis, &pNewSess, pThis->pSocksLstn[i+1]);
620 --nfds; /* indicate we have processed one */
624 /* now check the sessions */
625 iTCPSess = TCPSessGetNxtSess(pThis, -1);
626 while(nfds && iTCPSess != -1) {
629 fdSess = pThis->pSessions[iTCPSess]->sock; // TODO: not clean, use method
630 if(FD_ISSET(fdSess, &readfds)) {
632 dbgprintf("tcp session socket with new data: #%d\n", fdSess);
634 /* Receive message */
635 state = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf));
637 pThis->pOnRegularClose(pThis->pSessions[iTCPSess]);
638 tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
639 } else if(state == -1) {
640 errmsg.LogError(NO_ERRCODE, "TCP session %d will be closed, error ignored\n", fdSess);
641 pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
642 tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
644 /* valid data received, process it! */
645 if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, state) != RS_RET_OK) {
646 /* in this case, something went awfully wrong.
647 * We are instructed to terminate the session.
649 errmsg.LogError(NO_ERRCODE, "Tearing down TCP Session %d - see "
650 "previous messages for reason(s)\n", iTCPSess);
651 pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
652 tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
655 --nfds; /* indicate we have processed one */
657 iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
665 /* Standard-Constructor
667 BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
668 pThis->pSocksLstn = NULL;
669 pThis->iSessMax = 200; /* TODO: useful default ;) */
670 ENDobjConstruct(tcpsrv)
673 /* ConstructionFinalizer
676 tcpsrvConstructFinalize(tcpsrv_t __attribute__((unused)) *pThis)
679 ISOBJ_TYPE_assert(pThis, tcpsrv);
680 pThis->pSocksLstn = pThis->OpenLstnSocks(pThis);
686 /* destructor for the tcpsrv object */
687 BEGINobjDestruct(tcpsrv) /* be sure to specify the object type also in END and CODESTART macros! */
688 CODESTARTobjDestruct(tcpsrv)
689 if(pThis->OnDestruct != NULL)
690 pThis->OnDestruct(pThis->pUsr);
692 deinit_tcp_listener(pThis);
693 ENDobjDestruct(tcpsrv)
696 /* debugprint for the tcpsrv object */
697 BEGINobjDebugPrint(tcpsrv) /* be sure to specify the object type also in END and CODESTART macros! */
698 CODESTARTobjDebugPrint(tcpsrv)
699 ENDobjDebugPrint(tcpsrv)
703 SetCBIsPermittedHost(tcpsrv_t *pThis, int (*pCB)(struct sockaddr *addr, char *fromHostFQDN, void*, void*))
706 pThis->pIsPermittedHost = pCB;
711 SetCBRcvData(tcpsrv_t *pThis, int (*pRcvData)(tcps_sess_t*, char*, size_t))
714 pThis->pRcvData = pRcvData;
719 SetCBOnListenDeinit(tcpsrv_t *pThis, int (*pCB)(void*))
722 pThis->pOnListenDeinit = pCB;
727 SetCBOnSessAccept(tcpsrv_t *pThis, rsRetVal (*pCB)(tcpsrv_t*, tcps_sess_t*))
730 pThis->pOnSessAccept = pCB;
735 SetCBOnDestruct(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
738 pThis->OnDestruct = pCB;
743 SetCBOnSessConstructFinalize(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
746 pThis->OnSessConstructFinalize = pCB;
751 SetCBOnSessDestruct(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
754 pThis->pOnSessDestruct = pCB;
759 SetCBOnRegularClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
762 pThis->pOnRegularClose = pCB;
767 SetCBOnErrClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
770 pThis->pOnErrClose = pCB;
775 SetCBOpenLstnSocks(tcpsrv_t *pThis, int* (*pCB)(tcpsrv_t*))
778 pThis->OpenLstnSocks = pCB;
783 SetUsrP(tcpsrv_t *pThis, void *pUsr)
791 /* queryInterface function
792 * rgerhards, 2008-02-29
794 BEGINobjQueryInterface(tcpsrv)
795 CODESTARTobjQueryInterface(tcpsrv)
796 if(pIf->ifVersion != tcpsrvCURR_IF_VERSION) { /* check for current version, increment on each change */
797 ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
800 /* ok, we have the right interface, so let's fill it
801 * Please note that we may also do some backwards-compatibility
802 * work here (if we can support an older interface version - that,
803 * of course, also affects the "if" above).
805 pIf->DebugPrint = tcpsrvDebugPrint;
806 pIf->Construct = tcpsrvConstruct;
807 pIf->ConstructFinalize = tcpsrvConstructFinalize;
808 pIf->Destruct = tcpsrvDestruct;
810 pIf->SessAccept = SessAccept;
811 pIf->configureTCPListen = configureTCPListen;
812 pIf->create_tcp_socket = create_tcp_socket;
815 pIf->SetUsrP = SetUsrP;
816 pIf->SetCBIsPermittedHost = SetCBIsPermittedHost;
817 pIf->SetCBOpenLstnSocks = SetCBOpenLstnSocks;
818 pIf->SetCBRcvData = SetCBRcvData;
819 pIf->SetCBOnListenDeinit = SetCBOnListenDeinit;
820 pIf->SetCBOnSessAccept = SetCBOnSessAccept;
821 pIf->SetCBOnSessConstructFinalize = SetCBOnSessConstructFinalize;
822 pIf->SetCBOnSessDestruct = SetCBOnSessDestruct;
823 pIf->SetCBOnDestruct = SetCBOnDestruct;
824 pIf->SetCBOnRegularClose = SetCBOnRegularClose;
825 pIf->SetCBOnErrClose = SetCBOnErrClose;
828 ENDobjQueryInterface(tcpsrv)
832 * rgerhards, 2008-03-10
834 BEGINObjClassExit(tcpsrv, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
835 CODESTARTObjClassExit(tcpsrv)
836 /* release objects we no longer need */
837 objRelease(tcps_sess, DONT_LOAD_LIB);
838 objRelease(conf, CORE_COMPONENT);
839 objRelease(errmsg, CORE_COMPONENT);
840 objRelease(net, LM_NET_FILENAME);
841 ENDObjClassExit(tcpsrv)
844 /* Initialize our class. Must be called as the very first method
845 * before anything else is called inside this class.
846 * rgerhards, 2008-02-29
848 BEGINObjClassInit(tcpsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE class also in END MACRO! */
849 /* request objects we use */
850 CHKiRet(objUse(errmsg, CORE_COMPONENT));
851 CHKiRet(objUse(net, LM_NET_FILENAME));
852 CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB));
853 CHKiRet(objUse(conf, CORE_COMPONENT));
855 /* set our own handlers */
856 OBJSetMethodHandler(objMethod_DEBUGPRINT, tcpsrvDebugPrint);
857 OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, tcpsrvConstructFinalize);
858 ENDObjClassInit(tcpsrv)
861 /* --------------- here now comes the plumbing that makes as a library module --------------- */
866 /* de-init in reverse order! */
868 tcps_sessClassExit();
874 CODEqueryEtryPt_STD_LIB_QUERIES
880 *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
882 /* Initialize all classes that are in our module - this includes ourselfs */
883 CHKiRet(tcps_sessClassInit(pModInfo));
884 CHKiRet(tcpsrvClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */