| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351 |
- /** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
- * All rights reserved.
- *
- * License: GPL (version 3 or any later version).
- * See LICENSE for details.
- * END COPYRIGHT BLOCK **/
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <stdlib.h>
- #include <signal.h>
- #include "slap.h"
- #include "prcvar.h"
- #include "prlog.h" /* for PR_ASSERT */
- #include "fe.h"
- #include <sasl.h>
- #if defined(LINUX)
- #include <netinet/tcp.h> /* for TCP_CORK */
- #endif
- typedef Connection work_q_item;
- static void connection_threadmain( void );
- static void connection_add_operation(Connection* conn, Operation *op);
- static void connection_free_private_buffer(Connection *conn);
- static void op_copy_identity(Connection *conn, Operation *op);
- static void connection_set_ssl_ssf(Connection *conn);
- static int is_ber_too_big(const Connection *conn, ber_len_t ber_len);
- static void log_ber_too_big_error(const Connection *conn,
- ber_len_t ber_len, ber_len_t maxbersize);
- static PRStack *op_stack; /* stack of Slapi_Operation * objects so we don't have to malloc/free every time */
- static PRInt32 op_stack_size; /* size of op_stack */
- struct Slapi_op_stack {
- PRStackElem stackelem; /* must be first in struct for PRStack to work */
- Slapi_Operation *op;
- };
- static void add_work_q( work_q_item *, struct Slapi_op_stack * );
- static work_q_item *get_work_q( struct Slapi_op_stack ** );
- /*
- * We maintain a global work queue of items that have not yet
- * been handed off to an operation thread.
- */
- struct Slapi_work_q {
- PRStackElem stackelem; /* must be first in struct for PRStack to work */
- work_q_item *work_item;
- struct Slapi_op_stack *op_stack_obj;
- struct Slapi_work_q *next_work_item;
- };
- static struct Slapi_work_q *head_work_q= NULL; /* global work queue head */
- static struct Slapi_work_q *tail_work_q= NULL; /* global work queue tail */
- static PRLock *work_q_lock=NULL; /* protects head_conn_q and tail_conn_q */
- static PRCondVar *work_q_cv; /* used by operation threads to wait for work - when there is a conn in the queue waiting to be processed */
- static PRInt32 work_q_size; /* size of conn_q */
- static PRInt32 work_q_size_max; /* high water mark of work_q_size */
- #define WORK_Q_EMPTY (work_q_size == 0)
- static PRStack *work_q_stack; /* stack of work_q structs so we don't have to malloc/free every time */
- static PRInt32 work_q_stack_size; /* size of work_q_stack */
- static PRInt32 work_q_stack_size_max; /* max size of work_q_stack */
- static PRInt32 op_shutdown= 0; /* if non-zero, server is shutting down */
- #define LDAP_SOCKET_IO_BUFFER_SIZE 512 /* Size of the buffer we give to the I/O system for reads */
- static struct Slapi_work_q *
- create_work_q()
- {
- struct Slapi_work_q *work_q = (struct Slapi_work_q *)PR_StackPop(work_q_stack);
- if (!work_q) {
- work_q = (struct Slapi_work_q *)slapi_ch_malloc(sizeof(struct Slapi_work_q));
- } else {
- PR_AtomicDecrement(&work_q_stack_size);
- }
- return work_q;
- }
- static void
- destroy_work_q(struct Slapi_work_q **work_q)
- {
- if (work_q && *work_q) {
- (*work_q)->op_stack_obj = NULL;
- (*work_q)->work_item = NULL;
- PR_StackPush(work_q_stack, (PRStackElem *)*work_q);
- PR_AtomicIncrement(&work_q_stack_size);
- if (work_q_stack_size > work_q_stack_size_max) {
- work_q_stack_size_max = work_q_stack_size;
- }
- }
- }
- static struct Slapi_op_stack *
- connection_get_operation(void)
- {
- struct Slapi_op_stack *stack_obj = (struct Slapi_op_stack *)PR_StackPop(op_stack);
- if (!stack_obj) {
- stack_obj = (struct Slapi_op_stack *)slapi_ch_malloc(sizeof(struct Slapi_op_stack));
- stack_obj->op = operation_new( plugin_build_operation_action_bitmap( 0,
- plugin_get_server_plg() ));
- } else {
- PR_AtomicDecrement(&op_stack_size);
- if(!stack_obj->op){
- stack_obj->op = operation_new( plugin_build_operation_action_bitmap( 0,
- plugin_get_server_plg() ));
- } else {
- operation_init(stack_obj->op,
- plugin_build_operation_action_bitmap( 0, plugin_get_server_plg() ));
- }
- }
- return stack_obj;
- }
- static void
- connection_done_operation(Connection *conn, struct Slapi_op_stack *stack_obj)
- {
- operation_done(&(stack_obj->op), conn);
- PR_StackPush(op_stack, (PRStackElem *)stack_obj);
- PR_AtomicIncrement(&op_stack_size);
- }
- /*
- * We really are done with this connection. Get rid of everything.
- *
- * Note: this function should be called with conn->c_mutex already locked
- * or at a time when multiple threads are not in play that might touch the
- * connection structure.
- */
- void
- connection_done(Connection *conn)
- {
- connection_cleanup(conn);
- /* free the private content, the buffer has been freed by above connection_cleanup */
- slapi_ch_free((void**)&conn->c_private);
- if (NULL != conn->c_sb)
- {
- ber_sockbuf_free(conn->c_sb);
- }
- if (NULL != conn->c_mutex)
- {
- PR_DestroyMonitor(conn->c_mutex);
- }
- if (NULL != conn->c_pdumutex)
- {
- PR_DestroyLock(conn->c_pdumutex);
- }
- /* PAGED_RESULTS */
- pagedresults_cleanup_all(conn, 0);
- }
- /*
- * We're going to be making use of this connection again.
- * So, get rid of everything we can't make use of.
- *
- * Note: this function should be called with conn->c_mutex already locked
- * or at a time when multiple threads are not in play that might touch the
- * connection structure.
- */
- void
- connection_cleanup(Connection *conn)
- {
- int enable_listeners = 0;
- bind_credentials_clear( conn, PR_FALSE /* do not lock conn */,
- PR_TRUE /* clear external creds. */ );
- slapi_ch_free((void**)&conn->c_authtype);
- /* Call the plugin extension destructors */
- factory_destroy_extension(connection_type,conn,NULL/*Parent*/,&(conn->c_extension));
- /*
- * We hang onto these, since we can reuse them.
- * Sockbuf *c_sb;
- * PRLock *c_mutex;
- * PRLock *c_pdumutex;
- * Conn_private *c_private;
- */
- if (conn->c_prfd)
- {
- PR_Close(conn->c_prfd);
- enable_listeners = 1; /* re-enable listeners disabled due to no fds */
- }
- conn->c_sd= SLAPD_INVALID_SOCKET;
- conn->c_ldapversion= 0;
-
- conn->c_isreplication_session = 0;
- slapi_ch_free((void**)&conn->cin_addr );
- slapi_ch_free((void**)&conn->cin_destaddr );
- if ( conn->c_domain != NULL )
- {
- ber_bvecfree( conn->c_domain );
- conn->c_domain = NULL;
- }
- /* conn->c_ops= NULL; */
- conn->c_gettingber= 0;
- conn->c_currentber= NULL;
- conn->c_starttime= 0;
- conn->c_connid= 0;
- conn->c_opsinitiated= 0;
- conn->c_opscompleted= 0;
- conn->c_threadnumber= 0;
- conn->c_refcnt= 0;
- conn->c_idlesince= 0;
- conn->c_flags= 0;
- conn->c_needpw= 0;
- conn->c_prfd= NULL;
- /* c_ci stays as it is */
- conn->c_fdi= SLAPD_INVALID_SOCKET_INDEX;
- conn->c_next= NULL;
- conn->c_prev= NULL;
- conn->c_extension= NULL;
- conn->c_ssl_ssf = 0;
- conn->c_local_ssf = 0;
- conn->c_unix_local = 0;
- /* destroy any sasl context */
- sasl_dispose((sasl_conn_t**)&conn->c_sasl_conn);
- /* PAGED_RESULTS */
- handle_closed_connection(conn); /* Clean up sockbufs */
- pagedresults_cleanup(conn, 0 /* do not need to lock inside */);
- /* free the connection socket buffer */
- connection_free_private_buffer(conn);
- if (enable_listeners && !g_get_shutdown()) {
- ns_enable_listeners();
- }
- #ifdef ENABLE_NUNC_STANS
- /* even if !config_get_enable_nunc_stans, it is ok to set to 0 here */
- conn->c_ns_close_jobs = 0;
- #endif
- }
- /*
- * Callers of connection_reset() must hold the conn->c_mutex lock.
- */
- void
- connection_reset(Connection* conn, int ns, PRNetAddr * from, int fromLen, int is_SSL)
- {
- char *pTmp = is_SSL ? "SSL " : "";
- char *str_ip = NULL, *str_destip;
- char buf_ip[ 256 ], buf_destip[ 256 ];
- char *str_unknown = "unknown";
- int in_referral_mode = config_check_referral_mode();
- LDAPDebug( LDAP_DEBUG_CONNS, "new %sconnection on %d\n", pTmp, conn->c_sd, 0 );
- /* bump our count of connections and update SNMP stats */
- conn->c_connid = slapi_counter_increment(num_conns);
- if (! in_referral_mode) {
- slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsConnectionSeq);
- slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsConnections);
- }
- /*
- * get peer address (IP address of this client)
- */
- slapi_ch_free( (void**)&conn->cin_addr ); /* just to be conservative */
- if ( from->raw.family == PR_AF_LOCAL ) { /* ldapi */
- conn->cin_addr = (PRNetAddr *) slapi_ch_malloc( sizeof( PRNetAddr ) );
- PL_strncpyz(buf_ip, from->local.path, sizeof(from->local.path));
- memcpy( conn->cin_addr, from, sizeof( PRNetAddr ) );
- if (!buf_ip[0]) {
- PR_GetPeerName( conn->c_prfd, from );
- PL_strncpyz(buf_ip, from->local.path, sizeof(from->local.path));
- memcpy( conn->cin_addr, from, sizeof( PRNetAddr ) );
- if (!buf_ip[0]) {
- /* Cannot derive local address, need something for logging */
- PL_strncpyz(buf_ip, "local", sizeof(buf_ip));
- }
- }
- str_ip = buf_ip;
- } else if ( ((from->ipv6.ip.pr_s6_addr32[0] != 0) || /* from contains non zeros */
- (from->ipv6.ip.pr_s6_addr32[1] != 0) ||
- (from->ipv6.ip.pr_s6_addr32[2] != 0) ||
- (from->ipv6.ip.pr_s6_addr32[3] != 0)) ||
- ((conn->c_prfd != NULL) && (PR_GetPeerName( conn->c_prfd, from ) == 0)) )
- {
- conn->cin_addr = (PRNetAddr *) slapi_ch_malloc( sizeof( PRNetAddr ) );
- memcpy( conn->cin_addr, from, sizeof( PRNetAddr ) );
-
- if ( PR_IsNetAddrType( conn->cin_addr, PR_IpAddrV4Mapped ) ) {
- PRNetAddr v4addr;
- memset( &v4addr, 0, sizeof( v4addr ) );
- v4addr.inet.family = PR_AF_INET;
- v4addr.inet.ip = conn->cin_addr->ipv6.ip.pr_s6_addr32[3];
- PR_NetAddrToString( &v4addr, buf_ip, sizeof( buf_ip ) );
- } else {
- PR_NetAddrToString( conn->cin_addr, buf_ip, sizeof( buf_ip ) );
- }
- buf_ip[ sizeof( buf_ip ) - 1 ] = '\0';
- str_ip = buf_ip;
- } else {
- /* try syscall since "from" was not given and PR_GetPeerName failed */
- /* a corner case */
- struct sockaddr_in addr; /* assuming IPv4 */
- #if ( defined( hpux ) )
- int addrlen;
- #else
- socklen_t addrlen;
- #endif
- addrlen = sizeof( addr );
- memset( &addr, 0, addrlen );
- if ( (conn->c_prfd == NULL) &&
- (getpeername( conn->c_sd, (struct sockaddr *)&addr, &addrlen ) == 0) )
- {
- conn->cin_addr = (PRNetAddr *)slapi_ch_malloc( sizeof( PRNetAddr ));
- memset( conn->cin_addr, 0, sizeof( PRNetAddr ) );
- PR_NetAddrFamily( conn->cin_addr ) = AF_INET6;
- /* note: IPv4-mapped IPv6 addr does not work on Windows */
- PR_ConvertIPv4AddrToIPv6(addr.sin_addr.s_addr, &(conn->cin_addr->ipv6.ip));
- PRLDAP_SET_PORT(conn->cin_addr, addr.sin_port);
- /* copy string equivalent of address into a buffer to use for
- * logging since each call to inet_ntoa() returns a pointer to a
- * single thread-specific buffer (which prevents us from calling
- * inet_ntoa() twice in one call to slapi_log_access()).
- */
- str_ip = inet_ntoa( addr.sin_addr );
- strncpy( buf_ip, str_ip, sizeof( buf_ip ) - 1 );
- buf_ip[ sizeof( buf_ip ) - 1 ] = '\0';
- str_ip = buf_ip;
- } else {
- str_ip = str_unknown;
- }
- }
- /*
- * get destination address (server IP address this client connected to)
- */
- slapi_ch_free( (void**)&conn->cin_destaddr ); /* just to be conservative */
- if ( conn->c_prfd != NULL ) {
- conn->cin_destaddr = (PRNetAddr *) slapi_ch_malloc( sizeof( PRNetAddr ) );
- memset( conn->cin_destaddr, 0, sizeof( PRNetAddr ));
- if (PR_GetSockName( conn->c_prfd, conn->cin_destaddr ) == 0) {
- if ( conn->cin_destaddr->raw.family == PR_AF_LOCAL ) { /* ldapi */
- PL_strncpyz(buf_destip, conn->cin_destaddr->local.path,
- sizeof(conn->cin_destaddr->local.path));
- if (!buf_destip[0]) {
- PL_strncpyz(buf_destip, "unknown local file", sizeof(buf_destip));
- }
- } else if ( PR_IsNetAddrType( conn->cin_destaddr, PR_IpAddrV4Mapped ) ) {
- PRNetAddr v4destaddr;
- memset( &v4destaddr, 0, sizeof( v4destaddr ) );
- v4destaddr.inet.family = PR_AF_INET;
- v4destaddr.inet.ip = conn->cin_destaddr->ipv6.ip.pr_s6_addr32[3];
- PR_NetAddrToString( &v4destaddr, buf_destip, sizeof( buf_destip ) );
- } else {
- PR_NetAddrToString( conn->cin_destaddr, buf_destip, sizeof( buf_destip ) );
- }
- buf_destip[ sizeof( buf_destip ) - 1 ] = '\0';
- str_destip = buf_destip;
- } else {
- str_destip = str_unknown;
- }
- } else {
- /* try syscall since c_prfd == NULL */
- /* a corner case */
- struct sockaddr_in destaddr; /* assuming IPv4 */
- #if ( defined( hpux ) )
- int destaddrlen;
- #else
- socklen_t destaddrlen;
- #endif
- destaddrlen = sizeof( destaddr );
- memset( &destaddr, 0, destaddrlen );
- if ( (getsockname( conn->c_sd, (struct sockaddr *)&destaddr, &destaddrlen ) == 0) ) {
- conn->cin_destaddr = (PRNetAddr *)slapi_ch_malloc( sizeof( PRNetAddr ));
- memset( conn->cin_destaddr, 0, sizeof( PRNetAddr ));
- PR_NetAddrFamily( conn->cin_destaddr ) = AF_INET6;
- PRLDAP_SET_PORT( conn->cin_destaddr, destaddr.sin_port );
- /* note: IPv4-mapped IPv6 addr does not work on Windows */
- PR_ConvertIPv4AddrToIPv6(destaddr.sin_addr.s_addr, &(conn->cin_destaddr->ipv6.ip));
- /* copy string equivalent of address into a buffer to use for
- * logging since each call to inet_ntoa() returns a pointer to a
- * single thread-specific buffer (which prevents us from calling
- * inet_ntoa() twice in one call to slapi_log_access()).
- */
- str_destip = inet_ntoa( destaddr.sin_addr );
- strncpy( buf_destip, str_destip, sizeof( buf_destip ) - 1 );
- buf_destip[ sizeof( buf_destip ) - 1 ] = '\0';
- str_destip = buf_destip;
- } else {
- str_destip = str_unknown;
- }
- }
- if ( !in_referral_mode ) {
- /* create a sasl connection */
- ids_sasl_server_new(conn);
- }
- /* log useful stuff to our access log */
- slapi_log_access( LDAP_DEBUG_STATS,
- "conn=%" NSPRIu64 " fd=%d slot=%d %sconnection from %s to %s\n",
- conn->c_connid, conn->c_sd, ns, pTmp, str_ip, str_destip );
- /* initialize the remaining connection fields */
- conn->c_ldapversion = LDAP_VERSION3;
- conn->c_starttime = current_time();
- conn->c_idlesince = conn->c_starttime;
- conn->c_flags = is_SSL ? CONN_FLAG_SSL : 0;
- conn->c_authtype = slapi_ch_strdup(SLAPD_AUTH_NONE);
- /* Just initialize the SSL SSF to 0 now since the handshake isn't complete
- * yet, which prevents us from getting the effective key length. */
- conn->c_ssl_ssf = 0;
- conn->c_local_ssf = 0;
- }
- /* Create a pool of threads for handling the operations */
- void
- init_op_threads()
- {
- int i;
- PRErrorCode errorCode;
- int max_threads = config_get_threadnumber();
- /* Initialize the locks and cv */
- if ((work_q_lock = PR_NewLock()) == NULL ) {
- errorCode = PR_GetError();
- LDAPDebug( LDAP_DEBUG_ANY,
- "init_op_threads: PR_NewLock failed for work_q_lock, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
- errorCode, slapd_pr_strerror(errorCode), 0 );
- exit(-1);
- }
- if ((work_q_cv = PR_NewCondVar( work_q_lock )) == NULL) {
- errorCode = PR_GetError();
- LDAPDebug( LDAP_DEBUG_ANY, "init_op_threads: PR_NewCondVar failed for work_q_cv, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
- errorCode, slapd_pr_strerror(errorCode), 0 );
- exit(-1);
- }
- work_q_stack = PR_CreateStack("connection_work_q");
- op_stack = PR_CreateStack("connection_operation");
- /* start the operation threads */
- for (i=0; i < max_threads; i++) {
- PR_SetConcurrency(4);
- if (PR_CreateThread (PR_USER_THREAD,
- (VFP) (void *) connection_threadmain, NULL,
- PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
- PR_UNJOINABLE_THREAD,
- SLAPD_DEFAULT_THREAD_STACKSIZE ) == NULL )
- {
- int prerr = PR_GetError();
- LDAPDebug( LDAP_DEBUG_ANY, "PR_CreateThread failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
- prerr, slapd_pr_strerror( prerr ), 0 );
- } else {
- g_incr_active_threadcnt();
- }
- }
- }
- static void
- referral_mode_reply(Slapi_PBlock *pb)
- {
- struct slapdplugin *plugin;
- plugin = (struct slapdplugin *) slapi_ch_calloc(1, sizeof(struct slapdplugin));
- if (plugin!=NULL)
- {
- struct berval *urls[2], url;
- char *refer;
- refer = config_get_referral_mode();
- pb->pb_plugin = plugin;
- set_db_default_result_handlers(pb);
- urls[0] = &url;
- urls[1] = NULL;
- url.bv_val = refer;
- url.bv_len = refer ? strlen(refer) : 0;
- slapi_send_ldap_result(pb, LDAP_REFERRAL, NULL, NULL, 0, urls);
- slapi_ch_free((void **)&plugin);
- slapi_ch_free((void **)&refer);
- }
- }
- static int
- connection_need_new_password(const Connection *conn, const Operation *op, Slapi_PBlock *pb)
- {
- int r= 0;
- /*
- * add tag != LDAP_REQ_SEARCH to allow admin server 3.5 to do
- * searches when the user needs to reset
- * the pw the first time logon.
- * LP: 22 Dec 2000: Removing LDAP_REQ_SEARCH. It's very unlikely that AS 3.5 will
- * be used to manage DS5.0
- */
- if ( conn->c_needpw && op->o_tag != LDAP_REQ_MODIFY &&
- op->o_tag != LDAP_REQ_BIND && op->o_tag != LDAP_REQ_UNBIND &&
- op->o_tag != LDAP_REQ_ABANDON && op->o_tag != LDAP_REQ_EXTENDED)
- {
- slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
- slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d %s\n",
- pb->pb_conn->c_connid, pb->pb_op->o_opid,
- "UNPROCESSED OPERATION - need new password" );
- send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM,
- NULL, NULL, 0, NULL );
- r= 1;
- }
- return r;
- }
- static void
- connection_dispatch_operation(Connection *conn, Operation *op, Slapi_PBlock *pb)
- {
- int minssf = config_get_minssf();
- int minssf_exclude_rootdse = 0;
- #ifdef TCP_CORK
- int enable_nagle = config_get_nagle();
- int pop_cork = 0;
- #endif
- /* Get the effective key length now since the first SSL handshake should be complete */
- connection_set_ssl_ssf( conn );
- /* Copy the Connection DN and SSF into the operation struct */
- op_copy_identity( conn, op );
- #if defined(USE_OPENLDAP)
- if (slapi_operation_is_flag_set(op, OP_FLAG_REPLICATED)) {
- /* If it is replicated op, ignore the maxbersize. */
- ber_len_t maxbersize = 0;
- ber_sockbuf_ctrl(conn->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &maxbersize);
- }
- #endif
- /* If the minimum SSF requirements are not met, only allow
- * bind and extended operations through. The bind and extop
- * code will ensure that only SASL binds and startTLS are
- * allowed, which gives the connection a chance to meet the
- * SSF requirements. We also allow UNBIND and ABANDON.*/
- /*
- * If nsslapd-minssf-exclude-rootdse is on, we have to go to the
- * next step and check if the operation is against rootdse or not.
- * Once found it's not on rootdse, return LDAP_UNWILLING_TO_PERFORM there.
- */
- minssf_exclude_rootdse = config_get_minssf_exclude_rootdse();
- if (!minssf_exclude_rootdse &&
- (conn->c_sasl_ssf < minssf) && (conn->c_ssl_ssf < minssf) &&
- (conn->c_local_ssf < minssf) &&(op->o_tag != LDAP_REQ_BIND) &&
- (op->o_tag != LDAP_REQ_EXTENDED) && (op->o_tag != LDAP_REQ_UNBIND) &&
- (op->o_tag != LDAP_REQ_ABANDON)) {
- slapi_log_access( LDAP_DEBUG_STATS,
- "conn=%" NSPRIu64 " op=%d UNPROCESSED OPERATION"
- " - Insufficient SSF (local_ssf=%d sasl_ssf=%d ssl_ssf=%d)\n",
- conn->c_connid, op->o_opid, conn->c_local_ssf,
- conn->c_sasl_ssf, conn->c_ssl_ssf );
- send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
- "Minimum SSF not met.", 0, NULL );
- return;
- }
- /* If anonymous access is disabled and the connection is
- * not authenticated, only allow bind and extended operations.
- * We allow extended operations so one can do a startTLS prior
- * to binding to protect their credentials in transit.
- * We also allow UNBIND and ABANDON.
- *
- * If anonymous access is only allowed for root DSE searches,
- * we let SEARCH operations through as well. The search code
- * is responsible for checking if the operation is a root DSE
- * search. */
- if ((slapi_sdn_get_dn(&(op->o_sdn)) == NULL ) &&
- /* anon access off and something other than BIND, EXTOP, UNBIND or ABANDON */
- (((config_get_anon_access_switch() == SLAPD_ANON_ACCESS_OFF) && (op->o_tag != LDAP_REQ_BIND) &&
- (op->o_tag != LDAP_REQ_EXTENDED) && (op->o_tag != LDAP_REQ_UNBIND) && (op->o_tag != LDAP_REQ_ABANDON)) ||
- /* root DSE access only and something other than BIND, EXTOP, UNBIND, ABANDON, or SEARCH */
- ((config_get_anon_access_switch() == SLAPD_ANON_ACCESS_ROOTDSE) && (op->o_tag != LDAP_REQ_BIND) &&
- (op->o_tag != LDAP_REQ_EXTENDED) && (op->o_tag != LDAP_REQ_UNBIND) &&
- (op->o_tag != LDAP_REQ_ABANDON) && (op->o_tag != LDAP_REQ_SEARCH))))
- {
- slapi_log_access( LDAP_DEBUG_STATS,
- "conn=%" NSPRIu64 " op=%d UNPROCESSED OPERATION"
- " - Anonymous access not allowed\n",
- conn->c_connid, op->o_opid );
- send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL,
- "Anonymous access is not allowed.",
- 0, NULL );
- return;
- }
- /* process the operation */
- switch ( op->o_tag ) {
- case LDAP_REQ_BIND:
- operation_set_type(op,SLAPI_OPERATION_BIND);
- do_bind( pb );
- break;
- case LDAP_REQ_UNBIND:
- operation_set_type(op,SLAPI_OPERATION_UNBIND);
- do_unbind( pb );
- break;
- case LDAP_REQ_ADD:
- operation_set_type(op,SLAPI_OPERATION_ADD);
- do_add( pb );
- break;
- case LDAP_REQ_DELETE:
- operation_set_type(op,SLAPI_OPERATION_DELETE);
- do_delete( pb );
- break;
- case LDAP_REQ_MODRDN:
- operation_set_type(op,SLAPI_OPERATION_MODRDN);
- do_modrdn( pb );
- break;
- case LDAP_REQ_MODIFY:
- operation_set_type(op,SLAPI_OPERATION_MODIFY);
- do_modify( pb );
- break;
- case LDAP_REQ_COMPARE:
- operation_set_type(op,SLAPI_OPERATION_COMPARE);
- do_compare( pb );
- break;
- case LDAP_REQ_SEARCH:
- operation_set_type(op,SLAPI_OPERATION_SEARCH);
- /* On Linux we can use TCP_CORK to get us 5-10% speed benefit when one entry is returned */
- /* Nagle needs to be turned _on_, the default is _on_ on linux, in daemon.c */
- #ifdef TCP_CORK
- if (enable_nagle && !conn->c_unix_local)
- {
- int i = 1;
- int ret = setsockopt(conn->c_sd,IPPROTO_TCP,TCP_CORK,&i,sizeof(i));
- if (ret < 0) {
- LDAPDebug(LDAP_DEBUG_ANY, "Failed to set TCP_CORK on connection %" NSPRIu64 "\n",conn->c_connid, 0, 0);
- }
- pop_cork = 1;
- }
- #endif
- do_search( pb );
- #ifdef TCP_CORK
- if (pop_cork) {
- /* Clear TCP_CORK to flush any unsent data but only if not LDAPI*/
- int i = 0;
- int ret = setsockopt(conn->c_sd,IPPROTO_TCP,TCP_CORK,&i,sizeof(i));
- if (ret < 0) {
- LDAPDebug(LDAP_DEBUG_ANY, "Failed to clear TCP_CORK on connection %" NSPRIu64 "\n",conn->c_connid, 0, 0);
- }
- }
- #endif
- break;
- /* for some strange reason, the console is using this old obsolete
- * value for ABANDON so we have to support it until the console
- * get fixed
- * otherwise the console has VERY BAD performances when a fair amount
- * of entries are created in the DIT
- */
- case LDAP_REQ_ABANDON_30:
- case LDAP_REQ_ABANDON:
- operation_set_type(op,SLAPI_OPERATION_ABANDON);
- do_abandon( pb );
- break;
- case LDAP_REQ_EXTENDED:
- operation_set_type(op,SLAPI_OPERATION_EXTENDED);
- do_extended( pb );
- break;
- default:
- LDAPDebug( LDAP_DEBUG_ANY,
- "ignoring unknown LDAP request (conn=%" NSPRIu64 ", tag=0x%lx)\n",
- conn->c_connid, op->o_tag, 0 );
- break;
- }
- }
- /* this function should be called under c_mutex */
- int connection_release_nolock_ext (Connection *conn, int release_only)
- {
- if (conn->c_refcnt <= 0)
- {
- slapi_log_error(SLAPI_LOG_FATAL, "connection",
- "conn=%" NSPRIu64 " fd=%d Attempt to release connection that is not acquired\n",
- conn->c_connid, conn->c_sd);
- PR_ASSERT (PR_FALSE);
- return -1;
- }
- else
- {
- conn->c_refcnt--;
- if (!release_only && (conn->c_refcnt == 1) && (conn->c_flags & CONN_FLAG_CLOSING)) {
- /* if refcnt == 1 usually means only the active connection list has a ref */
- /* refcnt == 0 means conntable just dropped the last ref */
- ns_connection_post_io_or_closing(conn);
- }
- return 0;
- }
- }
- int connection_release_nolock (Connection *conn)
- {
- return connection_release_nolock_ext(conn, 0);
- }
- /* this function should be called under c_mutex */
- int connection_acquire_nolock_ext (Connection *conn, int allow_when_closing)
- {
- /* connection in the closing state can't be acquired */
- if (!allow_when_closing && (conn->c_flags & CONN_FLAG_CLOSING))
- {
- /* This may happen while other threads are still working on this connection */
- slapi_log_error(SLAPI_LOG_FATAL, "connection",
- "conn=%" NSPRIu64 " fd=%d Attempt to acquire connection in the closing state\n",
- conn->c_connid, conn->c_sd);
- return -1;
- }
- else
- {
- conn->c_refcnt++;
- return 0;
- }
- }
- int connection_acquire_nolock (Connection *conn) {
- return connection_acquire_nolock_ext(conn, 0);
- }
- /* returns non-0 if connection can be reused and 0 otherwise */
- int connection_is_free (Connection *conn)
- {
- int rc;
- PR_EnterMonitor(conn->c_mutex);
- rc = conn->c_sd == SLAPD_INVALID_SOCKET && conn->c_refcnt == 0 &&
- !(conn->c_flags & CONN_FLAG_CLOSING);
- PR_ExitMonitor(conn->c_mutex);
- return rc;
- }
- int connection_is_active_nolock (Connection *conn)
- {
- return (conn->c_sd != SLAPD_INVALID_SOCKET) &&
- !(conn->c_flags & CONN_FLAG_CLOSING);
- }
- /* The connection private structure for UNIX turbo mode */
- struct Conn_private
- {
- int previous_op_count; /* the operation counter value last time we sampled it, used to compute operation rate */
- int operation_rate; /* rate (ops/sample period) at which this connection has been processing operations */
- time_t previous_count_check_time; /* The wall clock time we last sampled the operation count */
- size_t c_buffer_size; /* size of the socket read buffer */
- char *c_buffer; /* pointer to the socket read buffer */
- size_t c_buffer_bytes; /* number of bytes currently stored in the buffer */
- size_t c_buffer_offset; /* offset to the location of new data in the buffer */
- int use_buffer; /* if true, use the buffer - if false, ber_get_next reads directly from socket */
- };
- #if defined(USE_OPENLDAP)
- /* Copy up to bytes_to_read bytes from b into return_buffer.
- * Returns a count of bytes copied (always >= 0).
- */
- ber_slen_t
- openldap_read_function(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
- {
- Connection *conn = NULL;
- /* copy up to bytes_to_read bytes into the caller's buffer, return the number of bytes copied */
- ber_slen_t bytes_to_copy = 0;
- char *readbuf; /* buffer to "read" from */
- size_t max; /* number of bytes currently stored in the buffer */
- size_t offset; /* offset to the location of new data in the buffer */
- PR_ASSERT(sbiod);
- PR_ASSERT(sbiod->sbiod_pvt);
- conn = (Connection *)sbiod->sbiod_pvt;
- if (CONNECTION_BUFFER_OFF == conn->c_private->use_buffer) {
- bytes_to_copy = PR_Recv(conn->c_prfd,buf,len,0,PR_INTERVAL_NO_WAIT);
- goto done;
- }
- PR_ASSERT(conn->c_private->c_buffer);
- readbuf = conn->c_private->c_buffer;
- max = conn->c_private->c_buffer_bytes;
- offset = conn->c_private->c_buffer_offset;
- if (len <= (max - offset)) {
- bytes_to_copy = len; /* we have enough buffered data */
- } else {
- bytes_to_copy = max - offset; /* just return what we have */
- }
- if (bytes_to_copy <= 0) {
- bytes_to_copy = 0; /* never return a negative result */
- /* in this case, we don't have enough data to satisfy the
- caller, so we have to let it know we need more */
- #if defined(EWOULDBLOCK)
- errno = EWOULDBLOCK;
- #elif defined(EAGAIN)
- errno = EAGAIN;
- #endif
- PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
- } else {
- /* copy buffered data into output buf */
- SAFEMEMCPY(buf, readbuf + offset, bytes_to_copy);
- conn->c_private->c_buffer_offset += bytes_to_copy;
- }
- done:
- return bytes_to_copy;
- }
- #endif
- int
- connection_new_private(Connection *conn)
- {
- if (NULL == conn->c_private) {
- Conn_private *new_private = (Conn_private *)slapi_ch_calloc(1,sizeof(Conn_private));
- if (NULL == new_private) {
- /* memory allocation failed */
- return -1;
- }
- conn->c_private = new_private;
- conn->c_private->use_buffer = config_get_connection_buffer();
- }
- /* The c_buffer is supposed to be NULL here, cleaned by connection_cleanup,
- double check to avoid memory leak */
- if ((CONNECTION_BUFFER_OFF != conn->c_private->use_buffer) && (NULL == conn->c_private->c_buffer)) {
- conn->c_private->c_buffer = (char*)slapi_ch_malloc(LDAP_SOCKET_IO_BUFFER_SIZE);
- if (NULL == conn->c_private->c_buffer) {
- /* memory allocation failure */
- return -1;
- }
- conn->c_private->c_buffer_size = LDAP_SOCKET_IO_BUFFER_SIZE;
- }
- /*
- * Clear the private structure, preserving the buffer and length in
- * case we are reusing the buffer.
- */
- {
- char *c_buffer = conn->c_private->c_buffer;
- size_t c_buffer_size = conn->c_private->c_buffer_size;
- int use_buffer = conn->c_private->use_buffer;
- memset( conn->c_private, 0, sizeof(Conn_private));
- conn->c_private->c_buffer = c_buffer;
- conn->c_private->c_buffer_size = c_buffer_size;
- conn->c_private->use_buffer = use_buffer;
- }
- return 0;
- }
- static void
- connection_free_private_buffer(Connection *conn)
- {
- if (NULL != conn->c_private) {
- slapi_ch_free((void*)&(conn->c_private->c_buffer));
- }
- }
- /*
- * Turbo Mode:
- * Turbo Connection Mode is designed to more efficiently
- * serve a small number of highly active connections performing
- * mainly search operations. It is only used on UNIX---completion
- * ports on NT make it unnecessary.
- * A connection can be in turbo mode, or not in turbo mode.
- * For non-turbo mode, the code path is the same as was before:
- * worker threads wait on a condition variable for work.
- * When they awake they consult the operation queue for
- * something to do, read the operation from the connection's socket,
- * perform the operation and go back to waiting on the condition variable.
- * In Turbo Mode, a worker thread becomes associated with a connection.
- * It then waits not on the condition variable, but directly on read ready
- * state on the connection's socket. When new data arrives, it decodes
- * the operation and executes it, and then goes back to read another
- * operation from the same socket, or block waiting on new data.
- * The read is done non-blocking, wait in poll with a timeout.
- *
- * There is a mechanism to ensure that only the most active
- * connections are in turbo mode at any time. If this were not
- * the case we could starve out some client operation requests
- * due to waiting on I/O in many turbo threads at the same time.
- *
- * Each worker thread periodically (every 10 seconds) examines
- * the activity level for the connection it is processing.
- * This applies regardless of whether the connection is
- * currently in turbo mode or not. Activity is measured as
- * the number of operations initiated since the last check was done.
- * The N connections with the highest activity level are allowed
- * to enter turbo mode. If the current connection is in the top N,
- * then we decide to enter turbo mode. If the current connection
- * is no longer in the top N, then we leave turbo mode.
- * The decision to enter or leave turbo mode is taken under
- * the connection mutex, preventing race conditions where
- * more than one thread can change the turbo state of a connection
- * concurrently.
- */
- /* Connection status values returned by
- connection_wait_for_new_work(), connection_read_operation(), etc. */
-
- #define CONN_FOUND_WORK_TO_DO 0
- #define CONN_SHUTDOWN 1
- #define CONN_NOWORK 2
- #define CONN_DONE 3
- #define CONN_TIMEDOUT 4
- #define CONN_TURBO_TIMEOUT_INTERVAL 1000 /* milliseconds */
- #define CONN_TURBO_CHECK_INTERVAL 5 /* seconds */
- #define CONN_TURBO_PERCENTILE 50 /* proportion of threads allowed to be in turbo mode */
- #define CONN_TURBO_HYSTERESIS 0 /* avoid flip flopping in and out of turbo mode */
- void connection_make_new_pb(Slapi_PBlock *pb, Connection *conn)
- {
- struct Slapi_op_stack *stack_obj = NULL;
- /* we used to malloc/free the pb for each operation - now, just use a local stack pb
- * in connection_threadmain, and just clear it out
- */
- /* *ppb = (Slapi_PBlock *) slapi_ch_calloc( 1, sizeof(Slapi_PBlock) ); */
- /* *ppb = slapi_pblock_new(); */
- pb->pb_conn = conn;
- stack_obj = connection_get_operation();
- pb->pb_op = stack_obj->op;
- pb->op_stack_elem = stack_obj;
- connection_add_operation( conn, pb->pb_op );
- }
- int connection_wait_for_new_work(Slapi_PBlock *pb, PRIntervalTime interval)
- {
- int ret = CONN_FOUND_WORK_TO_DO;
- work_q_item *wqitem = NULL;
- struct Slapi_op_stack *op_stack_obj = NULL;
- PR_Lock( work_q_lock );
- while( !op_shutdown && WORK_Q_EMPTY ) {
- PR_WaitCondVar( work_q_cv, interval );
- }
- if ( op_shutdown ) {
- LDAPDebug0Args( LDAP_DEBUG_TRACE, "connection_wait_for_new_work: shutdown\n" );
- ret = CONN_SHUTDOWN;
- } else if ( NULL == ( wqitem = get_work_q( &op_stack_obj ) ) ) {
- /* not sure how this can happen */
- LDAPDebug0Args( LDAP_DEBUG_TRACE, "connection_wait_for_new_work: no work to do\n" );
- ret = CONN_NOWORK;
- } else {
- /* make new pb */
- pb->pb_conn = (Connection *)wqitem;
- pb->op_stack_elem = op_stack_obj;
- pb->pb_op = op_stack_obj->op;
- }
- PR_Unlock( work_q_lock );
- return ret;
- }
- #ifdef USE_OPENLDAP
- #include "openldapber.h"
- #else
- #include "mozldap.h"
- #endif
- static ber_tag_t
- _ber_get_len(BerElement *ber, ber_len_t *lenp)
- {
- #ifdef USE_OPENLDAP
- OLBerElement *lber = (OLBerElement *)ber;
- #else
- MozElement *lber = (MozElement *)ber;
- #endif
- if (NULL == lenp) {
- return LBER_DEFAULT;
- }
- *lenp = 0;
- if (NULL == lber) {
- return LBER_DEFAULT;
- }
- *lenp = lber->ber_len;
- return lber->ber_tag;
- }
- /*
- * Utility function called by connection_read_operation(). This is a
- * small wrapper on top of libldap's ber_get_next_buffer_ext().
- *
- * Caller must hold conn->c_mutex
- *
- * Return value:
- * 0: Success
- * case 1) If there was not enough data in the buffer to complete the
- * message, go to the next cycle. In this case, bytes_scanned is set
- * to a positive number and *tagp is set to LBER_DEFAULT.
- * case 2) Complete. *tagp == (tag of the message) and bytes_scanned is
- * set to a positive number.
- * -1: Failure
- * case 1) *tagp == LBER_OVERFLOW: the length is either bigger than
- * ber_uint_t type or the value preset via
- * LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE option
- * case 2) *tagp == LBER_DEFAULT: memory error or tag mismatch
- */
- static int
- get_next_from_buffer( void *buffer, size_t buffer_size, ber_len_t *lenp,
- ber_tag_t *tagp, BerElement *ber, Connection *conn )
- {
- PRErrorCode err = 0;
- PRInt32 syserr = 0;
- ber_len_t bytes_scanned = 0;
- *lenp = 0;
- #if defined(USE_OPENLDAP)
- *tagp = ber_get_next( conn->c_sb, &bytes_scanned, ber );
- #else
- *tagp = ber_get_next_buffer_ext( buffer, buffer_size, lenp, ber,
- &bytes_scanned, conn->c_sb );
- #endif
- /* openldap ber_get_next doesn't return partial bytes_scanned if it hasn't
- read a whole pdu - so we have to check the errno for the
- "would block" condition meaning openldap needs more data to read */
- if ((LBER_OVERFLOW == *tagp || LBER_DEFAULT == *tagp) && 0 == bytes_scanned &&
- !SLAPD_SYSTEM_WOULD_BLOCK_ERROR(errno))
- {
- if ((LBER_OVERFLOW == *tagp) || (errno == ERANGE))
- {
- ber_len_t maxbersize = config_get_maxbersize();
- ber_len_t tmplen = 0;
- (void)_ber_get_len(ber, &tmplen);
- /* openldap does not differentiate between length == 0
- and length > max - all we know is that there was a
- problem with the length - assume too big */
- err = SLAPD_DISCONNECT_BER_TOO_BIG;
- log_ber_too_big_error(conn, tmplen, maxbersize);
- }
- else
- {
- err = SLAPD_DISCONNECT_BAD_BER_TAG;
- }
- syserr = errno;
- /* Bad stuff happened, like the client sent us some junk */
- LDAPDebug( LDAP_DEBUG_CONNS,
- "ber_get_next failed for connection %" NSPRIu64 "\n", conn->c_connid, 0, 0 );
- /* reset private buffer */
- conn->c_private->c_buffer_bytes = conn->c_private->c_buffer_offset = 0;
- /* drop connection */
- disconnect_server_nomutex( conn, conn->c_connid, -1, err, syserr );
- return -1;
- } else if (CONNECTION_BUFFER_OFF == conn->c_private->use_buffer) {
- *lenp = bytes_scanned;
- if ((LBER_OVERFLOW == *tagp || LBER_DEFAULT == *tagp) && 0 == bytes_scanned &&
- SLAPD_SYSTEM_WOULD_BLOCK_ERROR(errno)) {
- return -2; /* tells connection_read_operation we need to try again */
- }
- } else {
- /* openldap_read_function will advance c_buffer_offset */
- #if !defined(USE_OPENLDAP)
- /* success, or need to wait for more data */
- /* if openldap could not read a whole pdu, bytes_scanned will be zero -
- it does not return partial results */
- conn->c_private->c_buffer_offset += bytes_scanned;
- #endif
- }
- return 0;
- }
- /* Either read read data into the connection buffer, or fail with err set */
- static int
- connection_read_ldap_data(Connection *conn, PRInt32 *err)
- {
- int ret = 0;
- ret = PR_Recv(conn->c_prfd,conn->c_private->c_buffer,conn->c_private->c_buffer_size,0,PR_INTERVAL_NO_WAIT);
- if (ret < 0) {
- *err = PR_GetError();
- } else if (CONNECTION_BUFFER_ADAPT == conn->c_private->use_buffer) {
- if ((ret == conn->c_private->c_buffer_size) && (conn->c_private->c_buffer_size < BUFSIZ)) {
- /* we read exactly what we requested - there could be more that we could have read */
- /* so increase the buffer size */
- conn->c_private->c_buffer_size *= 2;
- if (conn->c_private->c_buffer_size > BUFSIZ) {
- conn->c_private->c_buffer_size = BUFSIZ;
- }
- conn->c_private->c_buffer = slapi_ch_realloc(conn->c_private->c_buffer, conn->c_private->c_buffer_size);
- }
- }
- return ret;
- }
- static size_t
- conn_buffered_data_avail_nolock(Connection *conn, int *conn_closed)
- {
- if ( (conn->c_sd == SLAPD_INVALID_SOCKET) || (conn->c_flags & CONN_FLAG_CLOSING) ) {
- /* connection is closed - ignore the buffer */
- *conn_closed = 1;
- return 0;
- } else {
- *conn_closed = 0;
- return conn->c_private->c_buffer_bytes - conn->c_private->c_buffer_offset;
- }
- }
- /* Upon returning from this function, we have either:
- 1. Read a PDU successfully.
- 2. Detected some error condition with the connection which requires closing it.
- 3. In Turbo mode, we Timed out without seeing any data.
- We also handle the case where we read ahead beyond the current PDU
- by buffering the data and setting the 'remaining_data' flag.
- */
- int connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *remaining_data)
- {
- ber_len_t len = 0;
- int ret = 0;
- int waits_done = 0;
- ber_int_t msgid;
- int new_operation = 1; /* Are we doing the first I/O read for a new operation ? */
- char *buffer = conn->c_private->c_buffer;
- PRErrorCode err = 0;
- PRInt32 syserr = 0;
- size_t buffer_data_avail;
- int conn_closed = 0;
- PR_EnterMonitor(conn->c_mutex);
- /*
- * if the socket is still valid, get the ber element
- * waiting for us on this connection. timeout is handled
- * in the low-level read_function.
- */
- if ( (conn->c_sd == SLAPD_INVALID_SOCKET) ||
- (conn->c_flags & CONN_FLAG_CLOSING) ) {
- ret = CONN_DONE;
- goto done;
- }
-
- *tag = LBER_DEFAULT;
- /* First check to see if we have buffered data from "before" */
- if ((buffer_data_avail = conn_buffered_data_avail_nolock(conn, &conn_closed))) {
- /* If so, use that data first */
- if ( 0 != get_next_from_buffer( buffer
- + conn->c_private->c_buffer_offset,
- buffer_data_avail,
- &len, tag, op->o_ber, conn )) {
- ret = CONN_DONE;
- goto done;
- }
- new_operation = 0;
- }
- /* If we still haven't seen a complete PDU, read from the network */
- while (*tag == LBER_DEFAULT) {
- int ioblocktimeout_waits = config_get_ioblocktimeout() / CONN_TURBO_TIMEOUT_INTERVAL;
- /* We should never get here with data remaining in the buffer */
- PR_ASSERT( !new_operation || !conn_buffered_data_avail_nolock(conn, &conn_closed));
- /* We make a non-blocking read call */
- if (CONNECTION_BUFFER_OFF != conn->c_private->use_buffer) {
- ret = connection_read_ldap_data(conn,&err);
- } else {
- ret = get_next_from_buffer( NULL, 0, &len, tag, op->o_ber, conn );
- if (ret == -1) {
- ret = CONN_DONE;
- goto done; /* get_next_from_buffer does the disconnect stuff */
- } else if (ret == 0) {
- ret = len;
- }
- *remaining_data = 0;
- }
- if (ret <= 0) {
- if (0 == ret) {
- /* Connection is closed */
- disconnect_server_nomutex( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BAD_BER_TAG, 0 );
- conn->c_gettingber = 0;
- signal_listner();
- ret = CONN_DONE;
- goto done;
- }
- /* err = PR_GetError(); */
- /* If we would block, we need to poll for a while */
- syserr = PR_GetOSError();
- if ( SLAPD_PR_WOULD_BLOCK_ERROR( err ) ||
- SLAPD_SYSTEM_WOULD_BLOCK_ERROR( syserr ) ) {
- struct PRPollDesc pr_pd;
- PRIntervalTime timeout = PR_MillisecondsToInterval(CONN_TURBO_TIMEOUT_INTERVAL);
- pr_pd.fd = (PRFileDesc *)conn->c_prfd;
- pr_pd.in_flags = PR_POLL_READ;
- pr_pd.out_flags = 0;
- ret = PR_Poll(&pr_pd, 1, timeout);
- waits_done++;
- /* Did we time out ? */
- if (0 == ret) {
- /* We timed out, should the server shutdown ? */
- if (op_shutdown) {
- ret = CONN_SHUTDOWN;
- goto done;
- }
- /* We timed out, is this the first read in a PDU ? */
- if (new_operation) {
- /* If so, we return */
- ret = CONN_TIMEDOUT;
- goto done;
- } else {
- /* Otherwise we loop, unless we exceeded the ioblock timeout */
- if (waits_done > ioblocktimeout_waits) {
- LDAPDebug( LDAP_DEBUG_CONNS,"ioblock timeout expired on connection %" NSPRIu64 "\n", conn->c_connid, 0, 0 );
- disconnect_server_nomutex( conn, conn->c_connid, -1,
- SLAPD_DISCONNECT_IO_TIMEOUT, 0 );
- ret = CONN_DONE;
- goto done;
- } else {
- /* The turbo mode may cause threads starvation.
- Do a yield here to reduce the starving.
- */
- PR_Sleep(PR_INTERVAL_NO_WAIT);
- continue;
- }
- }
- }
- if (-1 == ret) {
- /* PR_Poll call failed */
- err = PR_GetError();
- syserr = PR_GetOSError();
- LDAPDebug( LDAP_DEBUG_ANY,
- "PR_Poll for connection %" NSPRIu64 " returns %d (%s)\n", conn->c_connid, err, slapd_pr_strerror( err ) );
- /* If this happens we should close the connection */
- disconnect_server_nomutex( conn, conn->c_connid, -1, err, syserr );
- ret = CONN_DONE;
- goto done;
- }
- LDAPDebug( LDAP_DEBUG_CONNS,
- "connection %" NSPRIu64 " waited %d times for read to be ready\n", conn->c_connid, waits_done, 0 );
- } else {
- /* Some other error, typically meaning bad stuff */
- syserr = PR_GetOSError();
- LDAPDebug( LDAP_DEBUG_CONNS,
- "PR_Recv for connection %" NSPRIu64 " returns %d (%s)\n", conn->c_connid, err, slapd_pr_strerror( err ) );
- /* If this happens we should close the connection */
- disconnect_server_nomutex( conn, conn->c_connid, -1, err, syserr );
- ret = CONN_DONE;
- goto done;
- }
- } else {
- /* We read some data off the network, do something with it */
- if (CONNECTION_BUFFER_OFF != conn->c_private->use_buffer) {
- conn->c_private->c_buffer_bytes = ret;
- conn->c_private->c_buffer_offset = 0;
- if ( get_next_from_buffer( buffer,
- conn->c_private->c_buffer_bytes
- - conn->c_private->c_buffer_offset,
- &len, tag, op->o_ber, conn ) != 0 ) {
- ret = CONN_DONE;
- goto done;
- }
- }
- LDAPDebug( LDAP_DEBUG_CONNS,
- "connection %" NSPRIu64 " read %d bytes\n", conn->c_connid, ret, 0 );
- new_operation = 0;
- ret = CONN_FOUND_WORK_TO_DO;
- waits_done = 0; /* got some data: reset counter */
- }
- }
- /* If there is remaining buffered data, set the flag to tell the caller */
- if (conn_buffered_data_avail_nolock(conn, &conn_closed)) {
- *remaining_data = 1;
- } else if (conn_closed){
- /* connection closed */
- ret = CONN_DONE;
- goto done;
- }
- if ( *tag != LDAP_TAG_MESSAGE ) {
- /*
- * We received a non-LDAP message. Log and close connection.
- */
- LDAPDebug( LDAP_DEBUG_ANY,
- "conn=%" NSPRIu64 " received a non-LDAP message (tag 0x%lx, expected 0x%lx)\n",
- conn->c_connid, *tag, LDAP_TAG_MESSAGE );
- disconnect_server_nomutex( conn, conn->c_connid, -1,
- SLAPD_DISCONNECT_BAD_BER_TAG, EPROTO );
- ret = CONN_DONE;
- goto done;
- }
- if ( (*tag = ber_get_int( op->o_ber, &msgid ))
- != LDAP_TAG_MSGID ) {
- /* log, close and send error */
- LDAPDebug( LDAP_DEBUG_ANY,
- "conn=%" NSPRIu64 " unable to read tag for incoming request\n", conn->c_connid, 0, 0 );
- disconnect_server_nomutex( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BAD_BER_TAG, EPROTO );
- ret = CONN_DONE;
- goto done;
- }
- if(is_ber_too_big(conn,len)) {
- disconnect_server_nomutex( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BER_TOO_BIG, 0 );
- ret = CONN_DONE;
- goto done;
- }
- op->o_msgid = msgid;
- *tag = ber_peek_tag( op->o_ber, &len );
- switch ( *tag ) {
- case LBER_ERROR:
- case LDAP_TAG_LDAPDN: /* optional username, for CLDAP */
- /* log, close and send error */
- LDAPDebug( LDAP_DEBUG_ANY,
- "conn=%" NSPRIu64 " ber_peek_tag returns 0x%lx\n", conn->c_connid, *tag, 0 );
- disconnect_server_nomutex( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BER_PEEK, EPROTO );
- ret = CONN_DONE;
- goto done;
- default:
- break;
- }
- op->o_tag = *tag;
- done:
- PR_ExitMonitor(conn->c_mutex);
- return ret;
- }
- void connection_make_readable(Connection *conn)
- {
- PR_EnterMonitor(conn->c_mutex);
- conn->c_gettingber = 0;
- PR_ExitMonitor(conn->c_mutex);
- signal_listner();
- }
- void connection_make_readable_nolock(Connection *conn)
- {
- conn->c_gettingber = 0;
- LDAPDebug2Args(LDAP_DEBUG_CONNS, "making readable conn %" NSPRIu64 " fd=%d\n",
- conn->c_connid, conn->c_sd);
- if (!(conn->c_flags & CONN_FLAG_CLOSING)) {
- /* if the connection is closing, try the close in connection_release_nolock */
- ns_connection_post_io_or_closing(conn);
- }
- }
- /*
- * Figure out the operation completion rate for this connection
- */
- void connection_check_activity_level(Connection *conn)
- {
- int current_count = 0;
- int delta_count = 0;
- PR_EnterMonitor(conn->c_mutex);
- /* get the current op count */
- current_count = conn->c_opscompleted;
- /* compare to the previous op count */
- delta_count = current_count - conn->c_private->previous_op_count;
- /* delta is the rate, store that */
- conn->c_private->operation_rate = delta_count;
- /* store current count in the previous count slot */
- conn->c_private->previous_op_count = current_count;
- /* update the last checked time */
- conn->c_private->previous_count_check_time = current_time();
- PR_ExitMonitor(conn->c_mutex);
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " activity level = %d\n",conn->c_connid,delta_count,0);
- }
- typedef struct table_iterate_info_struct {
- int connection_count;
- int rank_count;
- int our_rate;
- } table_iterate_info;
- int table_iterate_function(Connection *conn, void *arg)
- {
- int ret = 0;
- table_iterate_info *pinfo = (table_iterate_info*)arg;
- pinfo->connection_count++;
- if (conn->c_private->operation_rate > pinfo->our_rate) {
- pinfo->rank_count++;
- }
- return ret;
- }
- /*
- * Scan the list of active connections, evaluate our relative rank
- * for connection activity.
- */
- void connection_find_our_rank(Connection *conn,int *connection_count, int *our_rank)
- {
- table_iterate_info info = {0};
- info.our_rate = conn->c_private->operation_rate;
- connection_table_iterate_active_connections(the_connection_table, &info, &table_iterate_function);
- *connection_count = info.connection_count;
- *our_rank = info.rank_count;
- }
- /*
- * Evaluate the turbo policy for this connection
- */
- void connection_enter_leave_turbo(Connection *conn, int current_turbo_flag, int *new_turbo_flag)
- {
- int current_mode = 0;
- int new_mode = 0;
- int connection_count = 0;
- int our_rank = 0;
- int threshold_rank = 0;
- PR_EnterMonitor(conn->c_mutex);
- /* We can already be in turbo mode, or not */
- current_mode = current_turbo_flag;
- if (pagedresults_in_use_nolock(conn)) {
- /* PAGED_RESULTS does not need turbo mode */
- new_mode = 0;
- } else if (conn->c_private->operation_rate == 0) {
- /* The connection is ranked by the passed activities. If some other
- * connection have more activity, increase rank by one. The highest
- * rank is least activity, good candidates to move out of turbo mode.
- * However, if no activity on all the connections, then every
- * connection gets 0 rank, so none move out.
- * No bother to do so much calcuation, short-cut to non-turbo mode
- * if no activities in passed interval */
- new_mode = 0;
- } else {
- double activet = 0.0;
- connection_find_our_rank(conn,&connection_count, &our_rank);
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " turbo rank = %d out of %d conns\n",conn->c_connid,our_rank,connection_count);
- activet = (double)g_get_active_threadcnt();
- threshold_rank = (int)(activet * ((double)CONN_TURBO_PERCENTILE / 100.0));
- /* adjust threshold_rank according number of connections,
- less turbo threads as more connections,
- one measure to reduce thread startvation.
- */
- if (connection_count > threshold_rank) {
- threshold_rank -= (connection_count - threshold_rank) / 5;
- }
- if (current_mode) {
- /* We're currently in turbo mode */
- /* Policy says that we stay in turbo mode provided
- connection activity is still high.
- */
- if (our_rank - CONN_TURBO_HYSTERESIS < threshold_rank) {
- /* Stay in turbo mode */
- new_mode = 1;
- } else {
- /* Exit turbo mode */
- new_mode = 0;
- }
- } else {
- /* We're currently not in turbo mode */
- /* Policy says that we go into turbo mode if
- recent connection activity is high.
- */
- if (our_rank + CONN_TURBO_HYSTERESIS < threshold_rank) {
- /* Enter turbo mode */
- new_mode = 1;
- } else {
- /* Stay out of turbo mode */
- new_mode = 0;
- }
- }
- }
- PR_ExitMonitor(conn->c_mutex);
- if (current_mode != new_mode) {
- if (current_mode) {
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " leaving turbo mode\n",conn->c_connid,0,0);
- } else {
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " entering turbo mode\n",conn->c_connid,0,0);
- }
- }
- *new_turbo_flag = new_mode;
- }
- static void
- connection_threadmain()
- {
- Slapi_PBlock local_pb;
- Slapi_PBlock *pb = &local_pb;
- /* wait forever for new pb until one is available or shutdown */
- PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT; /* PR_SecondsToInterval(10); */
- Connection *conn = NULL;
- Operation *op;
- ber_tag_t tag = 0;
- int need_wakeup = 0;
- int thread_turbo_flag = 0;
- int ret = 0;
- int more_data = 0;
- int replication_connection = 0; /* If this connection is from a replication supplier, we want to ensure that operation processing is serialized */
- int doshutdown = 0;
- int maxthreads = 0;
- int enable_nunc_stans = 0;
- long bypasspollcnt = 0;
- #ifdef ENABLE_NUNC_STANS
- enable_nunc_stans = config_get_enable_nunc_stans();
- #endif
- #if defined( hpux )
- /* Arrange to ignore SIGPIPE signals. */
- SIGNAL( SIGPIPE, SIG_IGN );
- #endif
- pblock_init(pb);
- while (1) {
- int is_timedout = 0;
- time_t curtime = 0;
-
- if( op_shutdown ) {
- LDAPDebug( LDAP_DEBUG_TRACE,
- "op_thread received shutdown signal\n", 0, 0, 0 );
- g_decr_active_threadcnt();
- return;
- }
- if (!thread_turbo_flag && !more_data) {
- /* If more data is left from the previous connection_read_operation,
- we should finish the op now. Client might be thinking it's
- done sending the request and wait for the response forever.
- [blackflag 624234] */
- ret = connection_wait_for_new_work(pb,interval);
- switch (ret) {
- case CONN_NOWORK:
- PR_ASSERT(interval != PR_INTERVAL_NO_TIMEOUT); /* this should never happen with PR_INTERVAL_NO_TIMEOUT */
- continue;
- case CONN_SHUTDOWN:
- LDAPDebug( LDAP_DEBUG_TRACE,
- "op_thread received shutdown signal\n", 0, 0, 0 );
- g_decr_active_threadcnt();
- return;
- case CONN_FOUND_WORK_TO_DO:
- /* note - don't need to lock here - connection should only
- be used by this thread - since c_gettingber is set to 1
- in connection_activity when the conn is added to the
- work queue, setup_pr_read_pds won't add the connection prfd
- to the poll list */
- if(pb->pb_conn->c_opscompleted == 0){
- /*
- * We have a new connection, set the anonymous reslimit idletimeout
- * if applicable.
- */
- char *anon_dn = config_get_anon_limits_dn();
- int idletimeout;
- /* If an anonymous limits dn is set, use it to set the limits. */
- if (anon_dn && (strlen(anon_dn) > 0)) {
- Slapi_DN *anon_sdn = slapi_sdn_new_normdn_byref( anon_dn );
- reslimit_update_from_dn( pb->pb_conn, anon_sdn );
- slapi_sdn_free( &anon_sdn );
- if (slapi_reslimit_get_integer_limit(pb->pb_conn,
- pb->pb_conn->c_idletimeout_handle,
- &idletimeout)
- == SLAPI_RESLIMIT_STATUS_SUCCESS)
- {
- pb->pb_conn->c_idletimeout = idletimeout;
- }
- }
- slapi_ch_free_string( &anon_dn );
- }
- if (connection_call_io_layer_callbacks(pb->pb_conn)) {
- LDAPDebug0Args( LDAP_DEBUG_ANY, "Error: could not add/remove IO layers from connection\n" );
- }
- default:
- break;
- }
- } else {
- /* The turbo mode may cause threads starvation.
- Do a yield here to reduce the starving
- */
- PR_Sleep(PR_INTERVAL_NO_WAIT);
- PR_EnterMonitor(conn->c_mutex);
- /* Make our own pb in turbo mode */
- connection_make_new_pb(pb,conn);
- if (connection_call_io_layer_callbacks(conn)) {
- LDAPDebug0Args( LDAP_DEBUG_ANY, "Error: could not add/remove IO layers from connection\n" );
- }
- PR_ExitMonitor(conn->c_mutex);
- if (! config_check_referral_mode()) {
- slapi_counter_increment(ops_initiated);
- slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsInOps);
- }
- }
- /* Once we're here we have a pb */
- conn = pb->pb_conn;
- op = pb->pb_op;
- maxthreads = config_get_maxthreadsperconn();
- more_data = 0;
- ret = connection_read_operation(conn, op, &tag, &more_data);
- if ((ret == CONN_DONE) || (ret == CONN_TIMEDOUT)) {
- slapi_log_error(SLAPI_LOG_CONNS, "connection_threadmain",
- "conn %" NSPRIu64 " read not ready due to %d - thread_turbo_flag %d more_data %d "
- "ops_initiated %d refcnt %d flags %d\n", conn->c_connid, ret, thread_turbo_flag, more_data,
- conn->c_opsinitiated, conn->c_refcnt, conn->c_flags);
- } else if (ret == CONN_FOUND_WORK_TO_DO) {
- slapi_log_error(SLAPI_LOG_CONNS, "connection_threadmain",
- "conn %" NSPRIu64 " read operation successfully - thread_turbo_flag %d more_data %d "
- "ops_initiated %d refcnt %d flags %d\n", conn->c_connid, thread_turbo_flag, more_data,
- conn->c_opsinitiated, conn->c_refcnt, conn->c_flags);
- }
- curtime = current_time();
- #define DB_PERF_TURBO 1
- #if defined(DB_PERF_TURBO)
- /* If it's been a while since we last did it ... */
- if (curtime - conn->c_private->previous_count_check_time > CONN_TURBO_CHECK_INTERVAL) {
- if (config_get_enable_turbo_mode()) {
- int new_turbo_flag = 0;
- /* Check the connection's activity level */
- connection_check_activity_level(conn);
- /* And if appropriate, change into or out of turbo mode */
- connection_enter_leave_turbo(conn,thread_turbo_flag,&new_turbo_flag);
- thread_turbo_flag = new_turbo_flag;
- } else {
- thread_turbo_flag = 0;
- }
- }
- /* turn off turbo mode immediately if any pb waiting in global queue */
- if (thread_turbo_flag && !WORK_Q_EMPTY) {
- thread_turbo_flag = 0;
- LDAPDebug2Args(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " leaving turbo mode - pb_q is not empty %d\n",
- conn->c_connid,work_q_size);
- }
- #endif
-
- switch (ret) {
- case CONN_DONE:
- /* This means that the connection was closed, so clear turbo mode */
- /*FALLTHROUGH*/
- case CONN_TIMEDOUT:
- thread_turbo_flag = 0;
- is_timedout = 1;
- /* In the case of CONN_DONE, more_data could have been set to 1
- * in connection_read_operation before an error was encountered.
- * In that case, we need to set more_data to 0 - even if there is
- * more data available, we're not going to use it anyway.
- * In the case of CONN_TIMEDOUT, it is only used in one place, and
- * more_data will never be set to 1, so it is safe to set it to 0 here.
- * We need more_data to be 0 so the connection will be processed
- * correctly at the end of this function.
- */
- more_data = 0;
- /* note:
- * should call connection_make_readable after the op is removed
- * connection_make_readable(conn);
- */
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " leaving turbo mode due to %d\n",
- conn->c_connid,ret,0);
- goto done;
- case CONN_SHUTDOWN:
- LDAPDebug( LDAP_DEBUG_TRACE,
- "op_thread received shutdown signal\n", 0, 0, 0 );
- g_decr_active_threadcnt();
- doshutdown = 1;
- goto done; /* To destroy pb, jump to done once */
- default:
- break;
- }
- /* if we got here, then we had some read activity */
- if (thread_turbo_flag) {
- /* turbo mode avoids handle_pr_read_ready which avoids setting c_idlesince
- update c_idlesince here since, if we got some read activity, we are
- not idle */
- conn->c_idlesince = curtime;
- }
- /*
- * Do not put the connection back to the read ready poll list
- * if the operation is unbind. Unbind will close the socket.
- * Similarly, if we are in turbo mode, don't send the socket
- * back to the poll set.
- * more_data: [blackflag 624234]
- * If the connection is from a replication supplier, don't make it readable here.
- * We want to ensure that replication operations are processed strictly in the order
- * they are received off the wire.
- */
- replication_connection = conn->c_isreplication_session;
- if ((tag != LDAP_REQ_UNBIND) && !thread_turbo_flag && !replication_connection) {
- if (!more_data) {
- conn->c_flags &= ~CONN_FLAG_MAX_THREADS;
- connection_make_readable_nolock(conn);
- /* once the connection is readable, another thread may access conn,
- * so need locking from here on */
- signal_listner();
- /* with nunc-stans, I see an enormous amount of time spent in the poll() in
- * connection_read_operation() when the below code is enabled - not sure why
- * nunc-stans makes such a huge difference - for now, just disable this code
- * when using nunc-stans - it is supposed to be an optimization but turns out
- * to not be the opposite with nunc-stans
- */
- } else if (!enable_nunc_stans) { /* more data in conn - just put back on work_q - bypass poll */
- bypasspollcnt++;
- PR_EnterMonitor(conn->c_mutex);
- /* don't do this if it would put us over the max threads per conn */
- if (conn->c_threadnumber < maxthreads) {
- /* for turbo, c_idlesince is set above - for !turbo and
- * !more_data, we put the conn back in the poll loop and
- * c_idlesince is set in handle_pr_read_ready - since we
- * are bypassing both of those, we set idlesince here
- */
- conn->c_idlesince = curtime;
- connection_activity(conn, maxthreads);
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " queued because more_data\n",
- conn->c_connid,0,0);
- } else {
- /* keep count of how many times maxthreads has blocked an operation */
- conn->c_maxthreadsblocked++;
- }
- PR_ExitMonitor(conn->c_mutex);
- }
- }
- /* are we in referral-only mode? */
- if (config_check_referral_mode() && tag != LDAP_REQ_UNBIND) {
- referral_mode_reply(pb);
- goto done;
- }
- /* check if new password is required */
- if(connection_need_new_password(conn, op, pb)) {
- goto done;
- }
- /* if this is a bulk import, only "add" and "import done"
- * are allowed */
- if (conn->c_flags & CONN_FLAG_IMPORT) {
- if ((tag != LDAP_REQ_ADD) && (tag != LDAP_REQ_EXTENDED)) {
- /* no cookie for you. */
- LDAPDebug(LDAP_DEBUG_ANY, "Attempted operation %d "
- "from within bulk import\n",
- tag, 0, 0);
- slapi_send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL,
- NULL, 0, NULL);
- goto done;
- }
- }
- /*
- * Call the do_<operation> function to process this request.
- */
- connection_dispatch_operation(conn, op, pb);
- done:
- if (doshutdown) {
- PR_EnterMonitor(conn->c_mutex);
- connection_remove_operation_ext(pb, conn, op);
- connection_make_readable_nolock(conn);
- conn->c_threadnumber--;
- slapi_counter_decrement(conns_in_maxthreads);
- slapi_counter_decrement(g_get_global_snmp_vars()->ops_tbl.dsConnectionsInMaxThreads);
- connection_release_nolock(conn);
- PR_ExitMonitor(conn->c_mutex);
- signal_listner();
- return;
- }
- /*
- * done with this operation. delete it from the op
- * queue for this connection, delete the number of
- * threads devoted to this connection, and see if
- * there's more work to do right now on this conn.
- */
- /* number of ops on this connection */
- PR_AtomicIncrement(&conn->c_opscompleted);
- /* total number of ops for the server */
- slapi_counter_increment(ops_completed);
- /* If this op isn't a persistent search, remove it */
- if ( pb->pb_op->o_flags & OP_FLAG_PS ) {
- PR_EnterMonitor(conn->c_mutex);
- connection_release_nolock (conn); /* psearch acquires ref to conn - release this one now */
- PR_ExitMonitor(conn->c_mutex);
- /* ps_add makes a shallow copy of the pb - so we
- * can't free it or init it here - just memset it to 0
- * ps_send_results will call connection_remove_operation_ext to free it
- */
- memset(pb, 0, sizeof(*pb));
- } else {
- /* delete from connection operation queue & decr refcnt */
- int conn_closed = 0;
- PR_EnterMonitor(conn->c_mutex);
- connection_remove_operation_ext( pb, conn, op );
- /* If we're in turbo mode, we keep our reference to the connection alive */
- /* can't use the more_data var because connection could have changed in another thread */
- more_data = conn_buffered_data_avail_nolock(conn, &conn_closed) ? 1 : 0;
- LDAPDebug(LDAP_DEBUG_CONNS,"conn %" NSPRIu64 " check more_data %d thread_turbo_flag %d\n",
- conn->c_connid,more_data,thread_turbo_flag);
- if (!more_data) {
- if (!thread_turbo_flag) {
- /*
- * Don't release the connection now.
- * But note down what to do.
- */
- if (replication_connection || (1 == is_timedout)) {
- connection_make_readable_nolock(conn);
- need_wakeup = 1;
- }
- if (!need_wakeup) {
- if (conn->c_threadnumber == maxthreads)
- need_wakeup = 1;
- else
- need_wakeup = 0;
- }
- if(conn->c_threadnumber == maxthreads){
- conn->c_flags &= ~CONN_FLAG_MAX_THREADS;
- slapi_counter_decrement(conns_in_maxthreads);
- slapi_counter_decrement(g_get_global_snmp_vars()->ops_tbl.dsConnectionsInMaxThreads);
- }
- conn->c_threadnumber--;
- connection_release_nolock(conn);
- /* Call signal_listner after releasing the
- * connection if required. */
- if (need_wakeup) {
- signal_listner();
- }
- } else if (1 == is_timedout) {
- connection_make_readable_nolock(conn);
- signal_listner();
- }
- }
- PR_ExitMonitor(conn->c_mutex);
- }
- } /* while (1) */
- }
- /* thread need to hold conn->c_mutex before calling this function */
- int
- connection_activity(Connection *conn, int maxthreads)
- {
- struct Slapi_op_stack *op_stack_obj;
- if (connection_acquire_nolock (conn) == -1) {
- LDAPDebug(LDAP_DEBUG_CONNS,
- "could not acquire lock in connection_activity as conn %" NSPRIu64 " closing fd=%d\n",
- conn->c_connid,conn->c_sd,0);
- /* XXX how to handle this error? */
- /* MAB: 25 Jan 01: let's return on error and pray this won't leak */
- return (-1);
- }
- /* set these here so setup_pr_read_pds will not add this conn back to the poll array */
- conn->c_gettingber = 1;
- conn->c_threadnumber++;
- if(conn->c_threadnumber == maxthreads){
- conn->c_flags |= CONN_FLAG_MAX_THREADS;
- conn->c_maxthreadscount++;
- slapi_counter_increment(max_threads_count);
- slapi_counter_increment(conns_in_maxthreads);
- slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsConnectionsInMaxThreads);
- slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsMaxThreadsHit);
- }
- op_stack_obj = connection_get_operation();
- connection_add_operation(conn, op_stack_obj->op);
- /* Add conn to the end of the work queue. */
- /* have to do this last - add_work_q will signal waiters in connection_wait_for_new_work */
- add_work_q( (work_q_item *)conn, op_stack_obj );
- if (! config_check_referral_mode()) {
- slapi_counter_increment(ops_initiated);
- slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsInOps);
- }
- return 0;
- }
- /* add_work_q(): will add a work_q_item to the end of the global work queue. The work queue
- is implemented as a single link list. */
- static void
- add_work_q( work_q_item *wqitem, struct Slapi_op_stack *op_stack_obj )
- {
- struct Slapi_work_q *new_work_q=NULL;
- LDAPDebug( LDAP_DEBUG_TRACE, "add_work_q \n", 0, 0, 0 );
- new_work_q = create_work_q();
- new_work_q->work_item = wqitem;
- new_work_q->op_stack_obj = op_stack_obj;
- new_work_q->next_work_item =NULL;
- PR_Lock( work_q_lock );
- if (tail_work_q == NULL) {
- tail_work_q = new_work_q;
- head_work_q = new_work_q;
- }
- else {
- tail_work_q->next_work_item = new_work_q;
- tail_work_q = new_work_q;
- }
- PR_AtomicIncrement( &work_q_size ); /* increment q size */
- if ( work_q_size > work_q_size_max ) {
- work_q_size_max = work_q_size;
- }
- PR_NotifyCondVar( work_q_cv ); /* notify waiters in connection_wait_for_new_work */
- PR_Unlock( work_q_lock );
- }
- /* get_work_q(): will get a work_q_item from the beginning of the work queue, return NULL if
- the queue is empty. This should only be called from connection_wait_for_new_work
- with the work_q_lock held */
- static work_q_item *
- get_work_q(struct Slapi_op_stack **op_stack_obj)
- {
- struct Slapi_work_q *tmp = NULL;
- work_q_item *wqitem;
- LDAPDebug0Args( LDAP_DEBUG_TRACE, "get_work_q \n" );
- if (head_work_q == NULL) {
- LDAPDebug0Args( LDAP_DEBUG_TRACE, "get_work_q: the work queue is empty.\n" );
- return NULL;
- }
- tmp = head_work_q;
- if ( head_work_q == tail_work_q ) {
- tail_work_q = NULL;
- }
- head_work_q = tmp->next_work_item;
- wqitem = tmp->work_item;
- *op_stack_obj = tmp->op_stack_obj;
- PR_AtomicDecrement( &work_q_size ); /* decrement q size */
- /* Free the memory used by the item found. */
- destroy_work_q(&tmp);
- return (wqitem);
- }
- /* Helper functions common to both varieties of connection code: */
- /* op_thread_cleanup() : This function is called by daemon thread when it gets
- the slapd_shutdown signal. It will set op_shutdown to 1 and notify
- all thread waiting on op_thread_cv to terminate. */
- void
- op_thread_cleanup()
- {
- LDAPDebug( LDAP_DEBUG_ANY,
- "slapd shutting down - signaling operation threads - op stack size %d max work q size %d max work q stack size %d\n",
- op_stack_size, work_q_size_max, work_q_stack_size_max);
- PR_AtomicIncrement(&op_shutdown);
- PR_Lock( work_q_lock );
- PR_NotifyAllCondVar ( work_q_cv ); /* tell any thread waiting in connection_wait_for_new_work to shutdown */
- PR_Unlock( work_q_lock );
- }
- /* do this after all worker threads have terminated */
- void
- connection_post_shutdown_cleanup()
- {
- struct Slapi_op_stack *stack_obj;
- int stack_cnt = 0;
- struct Slapi_work_q *work_q;
- int work_cnt = 0;
- while ((work_q = (struct Slapi_work_q *)PR_StackPop(work_q_stack))) {
- Connection *conn = (Connection *)work_q->work_item;
- stack_obj = work_q->op_stack_obj;
- if (stack_obj) {
- if (conn) {
- connection_remove_operation(conn, stack_obj->op);
- }
- connection_done_operation(conn, stack_obj);
- }
- slapi_ch_free((void **)&work_q);
- work_cnt++;
- }
- PR_DestroyStack(work_q_stack);
- work_q_stack = NULL;
- while ((stack_obj = (struct Slapi_op_stack *)PR_StackPop(op_stack))) {
- operation_free(&stack_obj->op, NULL);
- slapi_ch_free((void **)&stack_obj);
- stack_cnt++;
- }
- PR_DestroyStack(op_stack);
- op_stack = NULL;
- LDAPDebug2Args( LDAP_DEBUG_ANY,
- "slapd shutting down - freed %d work q stack objects - freed %d op stack objects\n",
- work_cnt, stack_cnt);
- }
- static void
- connection_add_operation(Connection* conn,Operation* op)
- {
- Operation **olist= &conn->c_ops;
- int id= conn->c_opsinitiated++;
- PRUint64 connid = conn->c_connid;
- Operation **tmp;
- /* slapi_ch_stop_recording(); */
- for ( tmp = olist; *tmp != NULL; tmp = &(*tmp)->o_next )
- ; /* NULL */
- *tmp= op;
- op->o_opid = id;
- op->o_connid = connid;
- /* Call the plugin extension constructors */
- op->o_extension = factory_create_extension(get_operation_object_type(),op,conn);
- }
- /*
- * Find an Operation on the Connection, and zap it in the butt.
- * Call this function with conn->c_mutex locked.
- */
- void
- connection_remove_operation( Connection *conn, Operation *op )
- {
- Operation **olist= &conn->c_ops;
- Operation **tmp;
- for ( tmp = olist; *tmp != NULL && *tmp != op; tmp = &(*tmp)->o_next )
- ; /* NULL */
- if ( *tmp == NULL )
- {
- LDAPDebug( LDAP_DEBUG_ANY, "connection_remove_operation: can't find op %d for conn %" NSPRIu64 "\n",
- (int)op->o_msgid, conn->c_connid, 0 );
- }
- else
- {
- *tmp = (*tmp)->o_next;
- }
- }
- void
- connection_remove_operation_ext( Slapi_PBlock *pb, Connection *conn, Operation *op )
- {
- connection_remove_operation(conn, op);
- connection_done_operation(conn, pb->op_stack_elem);
- pb->pb_op = NULL;
- slapi_pblock_init(pb);
- }
- /*
- * Return a non-zero value if any operations are pending on conn.
- * Operation op2ignore is ignored (okay to pass NULL). Typically, op2ignore
- * is the caller's op (because the caller wants to check if all other
- * ops are done).
- * If test_resultsent is non-zero, operations that have already sent
- * a result to the client are ignored.
- * Call this function with conn->c_mutex locked.
- */
- int
- connection_operations_pending( Connection *conn, Operation *op2ignore,
- int test_resultsent )
- {
- Operation *op;
- PR_ASSERT( conn != NULL );
- for ( op = conn->c_ops; op != NULL; op = op->o_next ) {
- if ( op == op2ignore ) {
- continue;
- }
- if ( !test_resultsent || op->o_status != SLAPI_OP_STATUS_RESULT_SENT ) {
- break;
- }
- }
- return( op != NULL );
- }
- /* Copy the authorization identity from the connection struct into the
- * operation struct. We do this late, because an operation might start
- * before authentication is complete, at least on an SSL connection.
- * We want each operation to get its authorization identity after the
- * SSL software has had its chance to finish the SSL handshake;
- * that is, after the first few bytes of the request are received.
- * In particular, we want the first request from an LDAPS client
- * to have an authorization identity derived from the initial SSL
- * handshake. We also copy the SSF at this time.
- */
- static void
- op_copy_identity(Connection *conn, Operation *op)
- {
- size_t dnlen;
- size_t typelen;
- PR_EnterMonitor(conn->c_mutex);
- dnlen= conn->c_dn ? strlen (conn->c_dn) : 0;
- typelen= conn->c_authtype ? strlen (conn->c_authtype) : 0;
- slapi_sdn_done(&op->o_sdn);
- slapi_ch_free_string(&(op->o_authtype));
- if (dnlen <= 0 && typelen <= 0) {
- op->o_authtype = NULL;
- } else {
- slapi_sdn_set_dn_byval(&op->o_sdn,conn->c_dn);
- op->o_authtype = slapi_ch_strdup(conn->c_authtype);
- /* set the thread data bind dn index */
- slapi_td_set_dn(slapi_ch_strdup(conn->c_dn));
- }
- /* XXX We should also copy c_client_cert into *op here; it's
- * part of the authorization identity. The operation's copy
- * (not c_client_cert) should be used for access control.
- */
- /* copy isroot flag as well so root DN privileges are preserved */
- op->o_isroot = conn->c_isroot;
- /* copy the highest SSF (between local, SASL, and SSL/TLS)
- * into the operation for use by access control. */
- if ((conn->c_sasl_ssf >= conn->c_ssl_ssf) && (conn->c_sasl_ssf >= conn->c_local_ssf)) {
- op->o_ssf = conn->c_sasl_ssf;
- } else if ((conn->c_ssl_ssf >= conn->c_sasl_ssf) && (conn->c_ssl_ssf >= conn->c_local_ssf)){
- op->o_ssf = conn->c_ssl_ssf;
- } else {
- op->o_ssf = conn->c_local_ssf;
- }
- PR_ExitMonitor(conn->c_mutex);
- }
- /* Sets the SSL SSF in the connection struct. */
- static void
- connection_set_ssl_ssf(Connection *conn)
- {
- PR_EnterMonitor(conn->c_mutex);
- if (conn->c_flags & CONN_FLAG_SSL) {
- SSL_SecurityStatus(conn->c_prfd, NULL, NULL, NULL, &(conn->c_ssl_ssf), NULL, NULL);
- } else {
- conn->c_ssl_ssf = 0;
- }
- PR_ExitMonitor(conn->c_mutex);
- }
- static int
- is_ber_too_big(const Connection *conn, ber_len_t ber_len)
- {
- ber_len_t maxbersize = config_get_maxbersize();
- if(ber_len > maxbersize)
- {
- log_ber_too_big_error(conn, ber_len, maxbersize);
- return 1;
- }
- return 0;
- }
- /*
- * Pass 0 for maxbersize if you do not have it handy. It is also OK to pass
- * 0 for ber_len, in which case a slightly less informative message is
- * logged.
- */
- static void
- log_ber_too_big_error(const Connection *conn, ber_len_t ber_len,
- ber_len_t maxbersize)
- {
- if (0 == maxbersize) {
- maxbersize = config_get_maxbersize();
- }
- if (0 == ber_len) {
- slapi_log_error( SLAPI_LOG_FATAL, "connection",
- "conn=%" NSPRIu64 " fd=%d Incoming BER Element was too long, max allowable"
- " is %" BERLEN_T " bytes. Change the nsslapd-maxbersize attribute in"
- " cn=config to increase.\n",
- conn->c_connid, conn->c_sd, maxbersize );
- } else {
- slapi_log_error( SLAPI_LOG_FATAL, "connection",
- "conn=%" NSPRIu64 " fd=%d Incoming BER Element was %" BERLEN_T " bytes, max allowable"
- " is %" BERLEN_T " bytes. Change the nsslapd-maxbersize attribute in"
- " cn=config to increase.\n",
- conn->c_connid, conn->c_sd, ber_len, maxbersize );
- }
- }
- void
- disconnect_server( Connection *conn, PRUint64 opconnid, int opid, PRErrorCode reason, PRInt32 error )
- {
- PR_EnterMonitor(conn->c_mutex);
- disconnect_server_nomutex( conn, opconnid, opid, reason, error );
- PR_ExitMonitor(conn->c_mutex);
- }
- static ps_wakeup_all_fn_ptr ps_wakeup_all_fn = NULL;
- /*
- * disconnect_server - close a connection. takes the connection to close,
- * the connid associated with the operation generating the close (so we
- * don't accidentally close a connection that's not ours), and the opid
- * of the operation generating the close (for logging purposes).
- */
- void
- disconnect_server_nomutex_ext( Connection *conn, PRUint64 opconnid, int opid, PRErrorCode reason, PRInt32 error, int schedule_closure_job )
- {
- if ( ( conn->c_sd != SLAPD_INVALID_SOCKET &&
- conn->c_connid == opconnid ) && !(conn->c_flags & CONN_FLAG_CLOSING) )
- {
- LDAPDebug(LDAP_DEBUG_CONNS, "setting conn %" NSPRIu64 " fd=%d "
- "to be disconnected: reason %d\n", conn->c_connid, conn->c_sd, reason);
- /*
- * PR_Close must be called before anything else is done because
- * of NSPR problem on NT which requires that the socket on which
- * I/O timed out is closed before any other I/O operation is
- * attempted by the thread.
- * WARNING : As of today the current code does not fulfill the
- * requirements above.
- */
- /* Mark that the socket should be closed on this connection.
- * We don't want to actually close the socket here, because
- * the listener thread could be PR_Polling over it right now.
- * The last thread to stop using the connection will do the closing.
- */
- conn->c_flags |= CONN_FLAG_CLOSING;
- g_decrement_current_conn_count();
- /*
- * Print the error captured above.
- */
- if (error && (EPIPE != error) ) {
- slapi_log_access( LDAP_DEBUG_STATS,
- "conn=%" NSPRIu64 " op=%d fd=%d closed error %d (%s) - %s\n",
- conn->c_connid, opid, conn->c_sd, error,
- slapd_system_strerror(error),
- slapd_pr_strerror(reason));
- } else {
- slapi_log_access( LDAP_DEBUG_STATS,
- "conn=%" NSPRIu64 " op=%d fd=%d closed - %s\n",
- conn->c_connid, opid, conn->c_sd,
- slapd_pr_strerror(reason));
- }
- if (! config_check_referral_mode()) {
- slapi_counter_decrement(g_get_global_snmp_vars()->ops_tbl.dsConnections);
- }
- conn->c_gettingber = 0;
- connection_abandon_operations( conn );
- /* needed here to ensure simple paged results timeout properly and
- * don't impact subsequent ops */
- pagedresults_reset_timedout_nolock(conn);
- if (! config_check_referral_mode()) {
- /*
- * If any of the outstanding operations on this
- * connection were persistent searches, then
- * ding all the persistent searches to get them
- * to notice that their operations have been abandoned.
- */
- int found_ps = 0;
- Operation *o;
- for ( o = conn->c_ops; !found_ps && o != NULL; o = o->o_next ) {
- if ( o->o_flags & OP_FLAG_PS ) {
- found_ps = 1;
- }
- }
- if ( found_ps ) {
- if ( NULL == ps_wakeup_all_fn ) {
- if ( get_entry_point( ENTRY_POINT_PS_WAKEUP_ALL,
- (caddr_t *)(&ps_wakeup_all_fn )) == 0 )
- {
- (ps_wakeup_all_fn)();
- }
- } else {
- (ps_wakeup_all_fn)();
- }
- }
- }
- if (schedule_closure_job) {
- ns_connection_post_io_or_closing(conn); /* make sure event loop wakes up and closes this conn */
- }
- } else {
- LDAPDebug2Args(LDAP_DEBUG_CONNS, "not setting conn %d to be disconnected: %s\n",
- conn->c_sd,
- (conn->c_sd == SLAPD_INVALID_SOCKET) ? "socket is invalid" :
- ((conn->c_connid != opconnid) ? "conn id does not match op conn id" :
- ((conn->c_flags & CONN_FLAG_CLOSING) ? "conn is closing" : "unknown")));
- }
- }
- void
- disconnect_server_nomutex( Connection *conn, PRUint64 opconnid, int opid, PRErrorCode reason, PRInt32 error )
- {
- disconnect_server_nomutex_ext(conn, opconnid, opid, reason, error, 1);
- }
- void
- connection_abandon_operations( Connection *c )
- {
- Operation *op;
- for ( op = c->c_ops; op != NULL; op = op->o_next )
- {
- /* abandon the operation only if it is not yet
- completed (i.e., no result has been sent yet to
- the client */
- /* sync repl uses the persist mode, and it cannot prevent
- * setting o_status, but has to be abandonned
- * handle it here until a better solution is found
- */
- if ( op->o_status != SLAPI_OP_STATUS_RESULT_SENT ||
- (op->o_flags & OP_FLAG_PS) ) {
- op->o_status = SLAPI_OP_STATUS_ABANDONED;
- }
- }
- }
- /* must be called within c->c_mutex */
- void
- connection_set_io_layer_cb( Connection *c, Conn_IO_Layer_cb push_cb, Conn_IO_Layer_cb pop_cb, void *cb_data )
- {
- c->c_push_io_layer_cb = push_cb;
- c->c_pop_io_layer_cb = pop_cb;
- c->c_io_layer_cb_data = cb_data;
- }
- /* must be called within c->c_mutex */
- int
- connection_call_io_layer_callbacks( Connection *c )
- {
- int rv = 0;
- if (c->c_pop_io_layer_cb) {
- rv = (c->c_pop_io_layer_cb)(c, c->c_io_layer_cb_data);
- c->c_pop_io_layer_cb = NULL;
- }
- if (!rv && c->c_push_io_layer_cb) {
- rv = (c->c_push_io_layer_cb)(c, c->c_io_layer_cb_data);
- c->c_push_io_layer_cb = NULL;
- }
- c->c_io_layer_cb_data = NULL;
- return rv;
- }
|