inline static bpt_node_t bpt_get_node_ptr(bpt_node_t n, int ndx) { if(n == NULL) { tb_error("node is null !!!\n"); return NULL; } if(ndx > n->cnt ) { tb_error("pnp: index out of bounds !!\n"); return NULL; } return (bpt_node_t)n->ptrs[ndx]; }
inline static tb_Key_t bpt_get_node_key(bpt_node_t n, int ndx) { if(n == NULL) { tb_error("node is null !!!\n"); return KINVAL; } if(ndx > n->cnt -1) { tb_error("pnk: index out of bounds !!\n"); return KINVAL; } return (tb_Key_t)n->keys[ndx]; }
rpc_sig_t Rpc_getMethod(Rpc_t Self, int methodId) { Pointer_t P = tb_Get(XRpc(Self)->methods, methodId); if(P) { return (rpc_sig_t)P2p(P); } else { tb_error("Rpc_getMethod[%d] : no such method !\n", methodId); } return NULL; }
Num_t tb_NumSet(Num_t N, int val) { if(tb_valid(N, TB_NUM, __FUNCTION__)) { void *p; if((p = tb_getMethod(N, OM_NUMSET))) { return ((Num_t(*)(Num_t, int val))p)(N, val); } else { tb_error("%p (%d:%s) [no numSet method]\n", N, N->isA, tb_nameOf(N->isA)); set_tb_errno(TB_ERR_NO_SUCH_METHOD); } } return NULL; }
int tb_recv_from(tb_listener_t *listener, tb_session_t *session) { session->last_trans = listener->protocol->f_recfrom(listener->sock_d, session->data, session->data_size, 0, (struct sockaddr*)session->addr_in, &session->addr_len); if(session->last_trans == -1) { tb_error(listener, "Error: recv_from", errno); listener->f_abort(listener); } return 0; }
int tb_send_to(tb_listener_t *listener, tb_session_t *session) { int rc; rc = listener->protocol->f_sendto(listener->sock_d, session->data, session->data_size, 0, (struct sockaddr*)session->addr_in, session->addr_len); if(rc == -1) { tb_error(listener, "Error: send_to", errno); listener->f_abort(listener); } return 0; }
int tb_create_socket(tb_listener_t *listener) { PRT_ACK("Create socket"); listener->sock_d = listener->protocol->f_sock(listener->addr_info->ai_family, listener->addr_info->ai_socktype, listener->addr_info->ai_protocol); if(listener->sock_d == -1) { tb_error(listener, "Error: socket", errno); listener->f_abort(listener); } return 0; }
int tb_listen(tb_listener_t *listener) { int rc = 0; tb_protocol_t *p = listener->protocol; rc = p->f_listen(listener->sock_d, 10); if(rc == -1) { tb_error(listener, "Error: listen : listen", errno); listener->f_abort(listener); } return rc; }
int tb_bind(tb_listener_t *listener) { int rc; rc = listener->protocol->f_bind(listener->sock_d, listener->addr_info->ai_addr, listener->addr_info->ai_addrlen); if(rc == -1) { tb_error(listener, "Error: bind", errno); listener->f_abort(listener); } return 0; }
int tb_connect(tb_listener_t *listener) { PRT_ACK("Connecting"); int rc; rc = listener->protocol->f_connect(listener->sock_d, listener->addr_info->ai_addr, listener->addr_info->ai_addrlen); if(rc == -1) { tb_error(listener, "Error: tb_connect", errno); listener->f_abort(listener); } return 0; }
Dict_t tb_dict_new(int keytype, int allow_dupes) { Dict_t O; pthread_once(&__class_registry_init_once, tb_classRegisterInit); if(kt_exists(keytype)) { O = tb_newParent(TB_DICT); O->isA = TB_DICT; O->members->instance = tb_xcalloc(1, sizeof(struct dict_extra)); ((struct dict_extra *)O->members->instance)->BplusTree = bpt_newTree(10, allow_dupes, keytype); if( fm->dbg) fm_addObject(O); return O; } tb_error("unknown/unregistered key type %d\n", keytype); return NULL; }
tb_session_t *tb_accept(tb_listener_t *listener) { tb_protocol_t *p = listener->protocol; tb_session_t *session = tb_create_server_session(); session->sock_d = p->f_accept(listener->sock_d, (struct sockaddr*)session->addr_in, &session->addr_len); if(session->sock_d == -1) { tb_error(listener, "Error: accept", errno); return session; } return session; }
/** read a line on a Socket_t using timeout and retries values as set in object. * \ingroup Socket * Target Socket_t must have been fully initialised by tb_Connect or tb_initServer. Read result is appended in 'msg' String_t. * * @return number of read bytes, or -1 if error * Internally read is buffered for best performances. The first line found in the buffer is appended to Msg String_t. The remaining buffered data (if any) will be used on next tb_readSockLine or tb_readSock. * * \remarks ERROR: * in case of error tb_errno will be set to : * - TB_ERR_INVALID_OBJECT : So not a TB_SOCKET * - TB_ERR_BAD : msg is not a TB_STRING * * @see tb_Socket, tb_writeSock, tb_writeSockBin, tb_readSockLine */ int tb_readSockLine(Socket_t S, String_t Msg) { fd_set set ; struct timeval tps ; int rc, retval; sock_members_t So; char buff[MAX_BUFFER]; no_error; if(! TB_VALID(S, TB_SOCKET)) { set_tb_errno(TB_ERR_INVALID_TB_OBJECT); return TB_ERR; } if(! TB_VALID(Msg, TB_STRING)) { set_tb_errno(TB_ERR_BAD); return TB_ERR; } So = XSock(S); if( So->buffer == NULL ) { So->buffer = tb_String(NULL); } else { if( tb_getSize(So->buffer) > 0 ) { char *s; if(( s = strchr(S2sz(So->buffer), '\n')) != NULL) { int len; len = 1+ (s - S2sz(So->buffer)); tb_StrnAdd(Msg, len, -1, "%s", S2sz(So->buffer)); tb_StrDel(So->buffer, 0, len); return len; } } } FD_ZERO(&set); FD_SET(So->sock, &set); tb_getSockTO(S, &(tps.tv_sec), &(tps.tv_usec)); restart_r_select: rc = select(So->sock+1, &set, NULL, NULL, &tps); switch (rc) { case -1: if( errno == EINTR ) goto restart_r_select; tb_error("tb_readSockLine[%d]: select failed (%s)\n", So->sock, strerror(errno)); // invalid fd ==> we're disconnected if( errno == EBADF ) So->status = TB_DISCONNECTED; set_tb_errno(TB_ERR_DISCONNECTED); retval = TB_ERR; break; case 0: /* Time out */ tb_notice("tb_readSockLine[%d]: select timed out\n", So->sock); So->status = TB_TIMEDOUT; retval = TB_ERR; break ; default: restart_r_read: #ifdef WITH_SSL if( So->ssl ) { rc = SSL_read(XSsl(S)->cx, buff, MAX_BUFFER-1); switch (SSL_get_error(XSsl(S)->cx,rc)) { case SSL_ERROR_NONE: buff[rc] = 0; tb_StrAdd(Msg, -1, "%s", buff); goto cutline; case SSL_ERROR_SYSCALL: if( errno ) { tb_error("tb_readSock[%d(SSL)]: read error (%s)\n", So->sock, strerror(errno)); } /* fall through */ case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SSL: ERR_print_errors_fp(stderr); return TB_ERR; } } else { set_nonblock_flag(So->sock, 1); rc = read(So->sock, buff, MAX_BUFFER-1); set_nonblock_flag(So->sock, 0); } #else set_nonblock_flag(So->sock, 1); rc = read(So->sock, buff, MAX_BUFFER-1); set_nonblock_flag(So->sock, 0); #endif #ifdef WITH_SSL cutline: #endif // WITH_SSL switch( rc ) { case -1: if( errno == EINTR|| errno == EWOULDBLOCK ) goto restart_r_read; if( errno != EWOULDBLOCK ) { tb_error("tb_readSockLine[%d]: read error (%s)\n", So->sock, strerror(errno)); retval = TB_ERR; So->status = TB_DISCONNECTED; break; } case 0: // eof tb_notice("tb_readSockLine[%d]: eof reached\n", So->sock); *buff = 0; tb_StrAdd(Msg, -1, "%S", So->buffer); retval = TB_ERR; tb_Clear(So->buffer); break; default: buff[rc] = 0; tb_StrAdd(So->buffer, -1, "%s", buff); { char *s = strchr(S2sz(So->buffer), '\n'); if( s ) { int len; len = 1+(s - S2sz(So->buffer)); tb_StrnAdd(Msg, len, -1, "%s", S2sz(So->buffer)); tb_StrDel(So->buffer, 0, len); retval = len; } else { goto restart_r_select; } } break; } break; } return retval; }
/** write binary on a Socket_t using timeout and retries values as set in object. * \ingroup Socket * Target Socket_t must have been fully initialised by tb_Connect or tb_initServer. * * @return number of written bytes, or -1 if error * * see test/srv_test.c , test/socket_test.c in build tree * * \remarks ERROR: * in case of error tb_errno will be set to : * - TB_ERR_INVALID_OBJECT : So not a TB_SOCKET * - TB_ERR_BAD : msg is not a TB_STRING * * @see tb_Socket, tb_writeSock, , tb_writeSockBin, tb_readSockLine * \todo fixme: use a pointer and a counter to remaining bytes to write for icomplete writes ... */ int tb_writeSockBin(Socket_t S, Raw_t raw) { fd_set set ; struct timeval tps ; int len, rc, remaining, retval = 0; char *msg; sock_members_t So; if(!TB_VALID(raw, TB_RAW)) { set_tb_errno(TB_ERR_BAD); return TB_KO; } if(!TB_VALID(S, TB_SOCKET)) { set_tb_errno(TB_ERR_INVALID_TB_OBJECT); return TB_ERR; } So = XSock(S); if(tb_getSockStatus(S) <= TB_DISCONNECTED) { return TB_ERR; } FD_ZERO(&set); FD_SET(So->sock,&set); msg = Raw_getData(raw); remaining = tb_getSize(raw); restart_w_select: tb_getSockTO(S, &tps.tv_sec, &tps.tv_usec); rc = select(FD_SETSIZE, NULL, &set, NULL, &tps); switch(rc) { case -1: if( errno == EINTR ) goto restart_w_select; tb_error("tb_writeSock[%d]: select failed (%s)\n", So->sock, strerror(errno)); // invalid fd ==> we're disconnected if( errno == EBADF || errno == ENOTSOCK || errno == ENOTCONN || errno == EPIPE ) So->status = TB_DISCONNECTED; retval = TB_ERR; break ; case 0: /* Time out */ tb_notice("tb_writeSock[%d]: select timed out\n", So->sock); So->status = TB_TIMEDOUT; retval = TB_KO; break ; default: if( ! FD_ISSET(So->sock, &set)) { tb_error("tb_readSock[%d]: select rc=%d but fd is not ready\n", So->sock, rc); retval = TB_KO; break ; } restart_w_write: #ifdef WITH_SSL if( So->ssl ) { rc=SSL_write(XSsl(S)->cx,msg , remaining); switch (SSL_get_error(XSsl(S)->cx,rc)) { case SSL_ERROR_NONE: return rc; case SSL_ERROR_SYSCALL: if ((rc != 0) && errno ) { tb_error("tb_writeSock[%d(SSL)]: write error (%s)\n", So->sock, strerror(errno)); } /* fall through */ case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SSL: default: ERR_print_errors_fp(stderr); return TB_ERR; } } else { set_nonblock_flag(So->sock, 1); rc = write(So->sock, msg, len = remaining); set_nonblock_flag(So->sock, 0); } #else set_nonblock_flag(So->sock, 1); rc = write(So->sock, msg, len = remaining); set_nonblock_flag(So->sock, 0); #endif switch( rc ) { case -1: if( errno == EINTR ) goto restart_w_write; tb_error("tb_writeSock[%d]: write error (%s)\n", So->sock, strerror(errno)); So->status = TB_DISCONNECTED; retval = TB_ERR; break; default: retval += rc; if( rc != remaining ) { tb_error("tb_writeSock[%d]: incomplet write (%d/%d)\n", So->sock, rc, len); msg += rc; remaining -= rc; goto restart_w_select; } break ; } break ; } return retval; }
/** read on a Socket_t using timeout and retries values as set in object. * \ingroup Socket * Target Socket_t must have been fully initialised by tb_Connect or tb_initServer. Read result is appended in 'msg' String_t. * * @return number of read bytes, or -1 if error. * Example: * \code * ... * String_t S = tb_String(NULL); * Socket_t So = tb_Socket(TB_TCP_UX, "/tmp/my_unix_sock", 0); * tb_Connect(So, 1, 1); * int rc; * * while(( rc = tb_readSock(So, S, 1024)) > 0); * switch( rc ) { * case -1: // error occurs * case 0: // read reached eof (or timed out) * ... * \endcode * S will contains a concatened string of all 1024's buffers read * * other Examples : * see test/srv_test.c , test/socket_test.c in build tree * * \remarks ERROR: * in case of error tb_errno will be set to : * - TB_ERR_INVALID_OBJECT : So not a TB_SOCKET * - TB_ERR_BAD : msg is not a TB_STRING * * @see tb_Socket, tb_writeSock, tb_writeSockBin, tb_readSockLine */ int tb_readSock(Socket_t S, tb_Object_t Msg, int maxlen) { fd_set set ; struct timeval tps ; int rc, retval = 0; sock_members_t So; char buff[maxlen+1]; no_error; if(! TB_VALID(S, TB_SOCKET)) { set_tb_errno(TB_ERR_INVALID_TB_OBJECT); return TB_ERR; } if(! TB_VALID(Msg, TB_STRING) && ! TB_VALID(Msg, TB_RAW)) { set_tb_errno(TB_ERR_BAD); return TB_ERR; } #ifdef WITH_XTI if( XSock(S)->addr_family == TB_X25 ) { return tb_readSock_X25(S, Msg, maxlen); } #endif So = XSock(S); if( So->buffer != NULL && tb_getSize(So->buffer) >0) { int n; n = TB_MIN( (maxlen), (tb_getSize(So->buffer))); tb_notice("readSock: already %d bytes in buffer\n", tb_getSize(So->buffer)); if( n ) { if( tb_isA(Msg) == TB_STRING) { tb_StrnAdd(Msg, n, -1, "%S", So->buffer); } else { tb_RawAdd(Msg, n, -1, S2sz(So->buffer)); } tb_StrDel(So->buffer, 0, n); if( n == maxlen) { return n; } else { maxlen -= n; retval = n; } } } restart_r_select: FD_ZERO(&set); FD_SET(So->sock, &set); tb_getSockTO(S, &(tps.tv_sec), &(tps.tv_usec)); rc = select(So->sock+1, &set, NULL, NULL, &tps); switch (rc) { case -1: if( errno == EINTR ) goto restart_r_select; tb_warn("tb_readSock[%d]: select failed (%s)\n", So->sock, strerror(errno)); // invalid fd ==> we're disconnected if( errno == EBADF ) So->status = TB_DISCONNECTED; set_tb_errno(TB_ERR_DISCONNECTED); retval = TB_ERR; break; case 0: /* Time out */ tb_notice("tb_readSock[%d]: select timed out\n", So->sock); So->status = TB_TIMEDOUT; retval = TB_KO; break ; default: if( ! FD_ISSET(So->sock, &set)) { tb_notice("tb_readSock[%d]: select rc=%d but fd is not ready\n", So->sock, rc); retval = TB_KO; break ; } restart_r_read: #ifdef WITH_SSL if( So->ssl ) { tb_info("SSL_read: try to read %d bytes\n", maxlen); rc = SSL_read(XSsl(S)->cx, buff, maxlen); switch (SSL_get_error(XSsl(S)->cx,rc)) { case SSL_ERROR_NONE: buff[rc] = 0; tb_StrAdd(Msg, -1, "%s", buff); if( rc < maxlen ) { tb_info("SSL_read: got only %d/%d\n", rc, maxlen); maxlen -= rc; goto restart_r_select; } return rc; case SSL_ERROR_SYSCALL: if( errno ) { tb_warn("tb_readSock[%d(SSL)]: read error (%s)\n", So->sock, strerror(errno)); } /* fall through */ case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SSL: ERR_print_errors_fp(stderr); return TB_ERR; } } else { set_nonblock_flag(So->sock, 1); rc = read(So->sock, buff, maxlen); set_nonblock_flag(So->sock, 0); } #else set_nonblock_flag(So->sock, 1); rc = read(So->sock, buff, maxlen); set_nonblock_flag(So->sock, 0); #endif switch( rc ) { case -1: if( errno == EINTR ) goto restart_r_read; if( errno != EWOULDBLOCK ) { tb_error("tb_readSock[%d]: read error (%s)\n", So->sock, strerror(errno)); retval = TB_ERR; So->status = TB_DISCONNECTED; break; } case 0: // eof *buff = 0; default: retval += rc; tb_RawAdd(Msg, rc, -1, buff); break ; } break; } return retval; }
retcode_t tb_initSSL(Socket_t S, enum ssl_mode mode, // SSL_CLIENT | SSL_SERVER ssl_meth_t method, // SSL1 | SSL2 | SSL3 | TLS1 char * CA_path, char * CA_file, char * cert, char * pwd, char * cipher) { SSL_METHOD * meth; sock_ssl_t m; tb_info("tb_initSSL in\n"); if(!TB_VALID(S, TB_SOCKET)) { set_tb_errno(TB_ERR_INVALID_TB_OBJECT); return TB_ERR; } if(XSock(S)->ssl != NULL ) { tb_warn("tb_initSSL: Socket_t allready SSL initialized\n"); set_tb_errno(TB_ERR_ALLREADY); return TB_ERR; } m = tb_xcalloc(1, sizeof(struct sock_ssl)); XSock(S)->ssl = m; m->ssl_method = method; m->mode = method; if( CA_path ) m->CA_path = tb_xstrdup(CA_path); if( CA_file ) m->CA_file = tb_xstrdup(CA_file); if( cert ) m->cert = tb_xstrdup(cert); if( pwd ) m->pwd = tb_xstrdup(pwd); if( cipher ) m->cipher = tb_xstrdup(cipher); __tb_init_SSL_once(); switch (m->ssl_method) { case 1: meth = (mode == SSL_CLIENT) ? SSLv23_client_method() : SSLv23_server_method(); break; case 2: meth = (mode == SSL_CLIENT) ? SSLv2_client_method() : SSLv2_server_method(); break; case 3: meth = (mode == SSL_CLIENT) ? SSLv3_client_method() : SSLv3_server_method(); break; case 4: meth = (mode == SSL_CLIENT) ? TLSv1_client_method() : TLSv1_server_method(); break; default: meth = NULL; goto err; } if (!(m->ctx = SSL_CTX_new(meth))) { tb_warn("tb_initSSL: Cannot create new SSL context\n"); ERR_print_errors_fp(stderr); XSock(S)->status = TB_BROKEN; return TB_ERR; } if(tb_errorlevel == TB_DEBUG) SSL_CTX_set_info_callback(m->ctx,info_cb); if(m->pwd) { SSL_CTX_set_default_passwd_cb(m->ctx, pass_cb); SSL_CTX_set_default_passwd_cb_userdata(m->ctx, S); } if(m->cert ) { if(SSL_CTX_use_certificate_file(m->ctx, m->cert, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); goto err; } if (SSL_CTX_use_PrivateKey_file(m->ctx, m->cert, SSL_FILETYPE_PEM) <= 0) { tb_error("tb_initSSL: Unable to get private key from '%s'\n", m->cert); ERR_print_errors_fp(stderr); goto err; } tb_info("privkey loaded\n"); if (!SSL_CTX_check_private_key(m->ctx)) { tb_error("tb_initSSL: Private key does not match the certificate public key\n"); goto err; } tb_info("tb_initSSL: privkey validated\n"); tb_info("tb_initSSL: certificate loaded\n"); } if(mode == SSL_CLIENT) { SSL_CTX_set_session_cache_mode(m->ctx, SSL_SESS_CACHE_CLIENT); } else { SSL_CTX_set_session_cache_mode(m->ctx, SSL_SESS_CACHE_SERVER); SSL_CTX_set_session_id_context(m->ctx, "try this one", 12); } if(m->CA_file || m->CA_path) { tb_info("tb_initSSL: loading CAs ...\n"); if(!SSL_CTX_load_verify_locations(m->ctx, m->CA_file, m->CA_path)) { XSock(S)->status = TB_BROKEN; tb_warn("tb_initSSL: Cannot load verify locations %s and %s\n", m->CA_file, m->CA_path); ERR_print_errors_fp(stderr); goto err; } tb_info("tb_initSSL: CA <%s/%s> loaded\n", m->CA_path, m->CA_file); SSL_CTX_set_verify(m->ctx, SSL_VERIFY_PEER, verify_cb); SSL_CTX_set_default_verify_paths(m->ctx); } /* Create and configure SSL connection. */ if (!(m->cx = (SSL *)SSL_new(m->ctx))) { tb_warn("tb_initSSL: Cannot create new SSL context\n"); ERR_print_errors_fp(stderr); goto err; } tb_info("tb_initSSL: ssl ctx initialized\n"); /* Use OpenSSL ciphers -v to see the cipher strings and their SSL * versions, key exchange, authentication, encryption, and message * digest algorithms, and key length restrictions. See the OpenSSL * for the syntax to combine ciphers, e.g. !SSLv2:RC4-MD5:RC4-SHA. * If you don't specify anything, you get the same as "DEFAULT", which * means "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP". */ if(m->cipher) { if(!SSL_CTX_set_cipher_list(m->ctx, m->cipher)) { tb_warn("tb_inittSSL: Cannot use cipher list %s\n", m->cipher); goto err; } tb_info("tb_initSSL: cipher set to <%s>\n", m->cipher); } tb_info("tb_initSSL out\n"); return TB_OK; err: // fixme: context not totally freed (CA_path, pwd ...) SSL_CTX_free(m->ctx); m->ctx = NULL; XSock(S)->status = TB_BROKEN; return TB_ERR; }
static int go_test(test_test_state *state) { int ret = TBEVENT_SUCCESS; if (state->loopdetect) return TBEVENT_SUCCESS; else state->loopdetect++; top: switch (state->state) { case 0: ret = act_test_state_0(state); break; case 1: ret = act_test_state_1(state); break; case 2: ret = act_test_state_2(state); break; case 3: ret = act_test_state_3(state); break; case 4: ret = act_test_state_4(state); break; case 5: ret = act_test_state_5(state); break; case 6: ret = act_test_state_6(state); break; case 7: ret = act_test_state_7(state); break; case 8: ret = act_test_state_8(state); break; case 9: ret = act_test_state_9(state); break; case 10: ret = act_test_state_10(state); break; case 11: ret = act_test_state_11(state); break; case 12: ret = act_test_state_12(state); break; case 13: ret = act_test_state_13(state); break; case 14: ret = act_test_state_14(state); break; case 15: ret = act_test_state_15(state); break; case 16: ret = act_test_state_16(state); break; case 17: ret = act_test_state_17(state); break; case 18: ret = act_test_state_18(state); break; case 19: ret = act_test_state_19(state); break; case 20: ret = act_test_state_20(state); break; case 21: ret = fin_test_state_21(state); break; case 22: ret = fin_test_state_22(state); break; case 23: ret = fin_test_state_23(state); break; case 24: ret = TBEVENT_FINISH; break; default: log_warnx("go_test: invalid state %d", state->state); ret = TBOX_API_INVALID; goto out; } if (ret == TBEVENT_BLOCKDONE || ret == TBEVENT_SUCCESS) { state->state++; if (state->state >= 0 && state->state <= 23) goto top; } else if (ret == TBEVENT_CALLBACK) { ret = TBEVENT_CALLBACK; goto out; } else if (ret == TBEVENT_FINISH) { if (state->state >= 0 && state->state < 21) state->state = 21; else state->state = 23 + 1; if (state->state >= 0 && state->state <= 23) goto top; } else if (ret != TBEVENT_SUCCESS) { if (state->state >= 0 && state->state < 21) state->state = 21; else state->state++; if (state->state >= 0 && state->state <= 23) goto top; } if (state->state >= 0 && state->state > 23) { if (state->cbfunc) ret = state->cbfunc(state->out.errorcode, state->cbarg, state->out.xid); free_test_state(state); free(state); state = NULL; } out: if (state) state->loopdetect--; if (ret == TBEVENT_SUCCESS || ret == TBEVENT_BLOCKDONE || ret == TBEVENT_CALLBACK) return ret; else log_warnx("test: %s", tb_error(ret)); return TBEVENT_BLOCKDONE; }