/** * Quit connection * @return CMD_RES_QUIT */ int conn_quit(XS_CONN *conn, int res) { switch (res) { case CMD_RES_CLOSED: log_info_conn("quit, closed by client"); break; case CMD_RES_IOERR: log_error_conn("quit, IO error (ERROR:%s)", strerror(errno)); break; case CMD_RES_NOMEM: log_error_conn("quit, out of memory"); break; case CMD_RES_TIMEOUT: log_warning_conn("quit, IO timeout (TIMEOUT:%d)", (int) conn->tv.tv_sec); break; case CMD_RES_STOPPED: log_notice_conn("quit, server stopped"); break; case CMD_RES_QUIT: log_info_conn("quit, normally"); break; case CMD_RES_ERROR: log_warning_conn("quit, result error (CODE:%d)", (int) conn->last_res); break; case CMD_RES_OTHER: default: log_warning_conn("quit, unknown reason (RES:%d)", res); break; } // flush all output buffer CONN_FLUSH(); // check to free zcmd if (conn->zcmd != NULL && (conn->flag & CONN_FLAG_ZMALLOC)) { debug_free(conn->zcmd); } // check to free cmds group conn_free_cmds(conn); // close socket & free-self close(CONN_FD()); debug_free(conn); conn_server.num_burst--; return CMD_RES_QUIT; }
void on_complete_work( uv_work_t *req, int status) { strus_connection_t* conn = (strus_connection_t*)( req->data); if (status == UV_ECANCELED) { log_message_conn( conn, "request execution canceled"); uv_close( (uv_handle_t*)&conn->tcp, on_close); } else if (status != 0) { log_error_conn( conn, "error in request, aborted"); uv_close( (uv_handle_t*)&conn->tcp, on_close); } else { int err; #ifdef STRUS_LOWLEVEL_DEBUG strus_hexdump( g_glbctx->logf, "ANSWER", conn->output, conn->outputsize); #endif err = uv_write( &conn->write_req, (uv_stream_t*)&conn->tcp, &conn->write_reqbuf, 1, on_write); if (err) { log_error_conn_sys( conn, "write error", err); uv_close( (uv_handle_t*)&conn->tcp, on_close); } #ifdef STRUS_LOWLEVEL_DEBUG else { log_message_conn( conn, "completed request"); } #endif } }
/** * Save the current zcmd into cmds list * @param conn * @return CMD_RES_CONT/CMD_RES_NOMEM */ static int conn_zcmd_save(XS_CONN *conn) { XS_CMDS *cmds; // change RETURN value to CONT log_debug_conn("save command into CMDS (CMD:%d)", conn->zcmd->cmd); debug_malloc(cmds, sizeof(XS_CMDS), XS_CMDS); if (cmds == NULL) { log_error_conn("failed to allocate memory for CMDS (SIZE:%d)", sizeof(XS_CMDS)); return CMD_RES_NOMEM; } cmds->next = NULL; if (conn->flag & CONN_FLAG_ZMALLOC) { // use zcmd directly conn->flag ^= CONN_FLAG_ZMALLOC; cmds->cmd = conn->zcmd; } else { // copy zcmd debug_malloc(cmds->cmd, XS_CMD_SIZE(conn->zcmd), XS_CMD); if (cmds->cmd != NULL) memcpy(cmds->cmd, conn->zcmd, XS_CMD_SIZE(conn->zcmd)); else { log_error_conn("failed to allocate memory for CMDS->cmd (CMD:%d, SIZE:%d)", conn->zcmd->cmd, XS_CMD_SIZE(conn->zcmd)); debug_free(cmds); return CMD_RES_NOMEM; } } // add the cmd to chain of cmds if (conn->zhead == NULL) conn->ztail = conn->zhead = cmds; else { conn->ztail->next = cmds; conn->ztail = cmds; } return CMD_RES_CONT; }
/** * Parse incoming data & execute commands * Called after data received. * @return CMD_RES_xxx (PAUSE|CONT|...quits...) */ int conn_cmds_parse(XS_CONN *conn, zcmd_exec_t func) { int off = 0, rc = CMD_RES_CONT; // check zcmd if (conn->zcmd != NULL) { if (conn->zcmd_left > 0 && conn->rcv_size > 0) { char *buf = XS_CMD_BUFTAIL(conn->zcmd) - conn->zcmd_left; off = (conn->zcmd_left > conn->rcv_size ? conn->rcv_size : conn->zcmd_left); memcpy(buf, conn->rcv_buf, off); log_debug_conn("copy rcv_buf to zcmd (SIZE:%d, RCV_SIZE:%d, ZCMD_LEFT:%d)", off, conn->rcv_size, conn->zcmd_left); conn->zcmd_left -= off; } // execute zcmd (otherwise: rcv_size - off <= 0) if (conn->zcmd_left == 0) rc = conn_zcmd_exec(conn, func); } // parse the cmd from rcv_buf while (rc == CMD_RES_CONT && (conn->rcv_size - off) >= sizeof(XS_CMD)) { conn->zcmd = (XS_CMD *) (conn->rcv_buf + off); off += sizeof(XS_CMD); log_debug_conn("get command {cmd:%d,arg1:%d,arg2:%d,blen1:%d,blen:%d}", conn->zcmd->cmd, conn->zcmd->arg1, conn->zcmd->arg2, conn->zcmd->blen1, conn->zcmd->blen); // check the zcmd is full or not if ((XS_CMD_BUFSIZE(conn->zcmd) + off) > conn->rcv_size) { XS_CMD *cmd; debug_malloc(cmd, XS_CMD_SIZE(conn->zcmd), XS_CMD); if (cmd == NULL) { log_error_conn("failed to allocate memory for ZCMD (CMD:%d, SIZE:%d)", conn->zcmd->cmd, XS_CMD_SIZE(conn->zcmd)); conn->zcmd = NULL; off -= sizeof(XS_CMD); // reset offset to cmd header rc = CMD_RES_NOMEM; } else { memcpy(cmd, conn->zcmd, conn->rcv_size - off + sizeof(XS_CMD)); conn->zcmd_left = XS_CMD_BUFSIZE(conn->zcmd) - (conn->rcv_size - off); conn->zcmd = cmd; conn->flag |= CONN_FLAG_ZMALLOC; // current zcmd must be free off = conn->rcv_size; log_debug_conn("wait left data of zcmd (CMD:%d, ZCMD_LEFT:%d)", cmd->cmd, conn->zcmd_left); } break; } // execute the zcmd (on rcv_buffer) off += XS_CMD_BUFSIZE(conn->zcmd); rc = conn_zcmd_exec(conn, func); } // flush the send buffer if (CONN_FLUSH() != 0) rc = CMD_RES_IOERR; // move the buffer conn->rcv_size -= off; if (conn->rcv_size > 0 && off > 0) memcpy(conn->rcv_buf, conn->rcv_buf + off, conn->rcv_size); // return the rc return rc; }
/** * Sample code of zcmd handler (last called in handlers) * @return CMD_RES_CONT (UNIMP,SAVE,CONT,IORERR,NOMEM,PAUSE,QUIT,OTHER,ERROR...) */ static int conn_zcmd_last(XS_CONN *conn) { int rc = CMD_RES_UNIMP; XS_CMD *cmd = conn->zcmd; // parse global command type [last] if (cmd->cmd == CMD_USE) { // buf=name, buf1=home char *name = XS_CMD_BUF(cmd); char *home = XS_CMD_BUF1(cmd); int name_len = XS_CMD_BLEN(cmd); int home_len = XS_CMD_BLEN1(cmd); XS_USER *user = xs_user_nget(name, name_len); log_debug_conn("load user from cache (USER:%p, NAME:%.*s)", user, name_len, name); if (user != NULL) { // replace new home directory if (home_len > 0 && home_len < sizeof(user->home)) { log_notice_conn("replace user home (NAME:%s, HOME:%.*s", user->name, home_len, home); memcpy(user->home, home, home_len); user->home[home_len] = '\0'; } } else { XS_USER new_user; rc = xs_user_check_name(name, name_len); if (rc == CMD_ERR_EMPTY) rc = CONN_RES_ERR(EMPTY); else if (rc == CMD_ERR_TOOLONG) rc = CONN_RES_ERR(TOOLONG); else if (rc == CMD_ERR_INVALIDCHAR) rc = CONN_RES_ERR(INVALIDCHAR); else { struct stat st; memset(&new_user, 0, sizeof(XS_USER)); memcpy(new_user.name, name, name_len); if (home_len == 0 || home_len >= sizeof(new_user.home)) sprintf(new_user.home, DEFAULT_DATA_DIR "%s", new_user.name); else { while (home_len > 1 && home[home_len - 1] == '/') home_len--; memcpy(new_user.home, home, home_len); } log_debug_conn("build new user (NAME:%s, HOME:%s)", new_user.name, new_user.home); // error check for home directory if (!stat(new_user.home, &st)) { // exists, but it is not a directory if (!S_ISDIR(st.st_mode)) { log_error_conn("invalid user home directory (HOME:%s)", new_user.home); return CONN_RES_ERR(INVALID_HOME); } } else if (mkdir(new_user.home, 0755) < 0) { // not exists, failed to create directory log_error_conn("failed to create user home (HOME:%s, ERROR:%s)", new_user.home, strerror(errno)); return CONN_RES_ERR(CREATE_HOME); } // save the user to memory cache if ((user = xs_user_put(&new_user)) == NULL) rc = CONN_RES_ERR(NOMEM); } } if (user != NULL) { log_info_conn("project changed (NAME:%s)", user->name); conn->user = user; conn->wdb = NULL; rc = CONN_RES_OK(PROJECT); } } return rc; }
static void on_read( uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { #else static void on_read( uv_stream_t* handle, ssize_t nread, uv_buf_t bufstruct) { const uv_buf_t* buf = &bufstruct; #endif strus_connection_t* conn = (strus_connection_t*)( handle->data); unsigned char* dp; unsigned int nn; unsigned int bufidx = 0; if (nread > 0) { switch (conn->state) { case CTX_READDATASIZE: dp = (unsigned char*)&conn->readbufsize + conn->hdrbytes; nn = (unsigned int)nread; if (nn > sizeof(int32_t) - conn->hdrbytes) { nn = sizeof(int32_t) - conn->hdrbytes; } conn->hdrbytes += nn; memcpy( dp, buf->base, nn); bufidx += nn; if (conn->hdrbytes == sizeof(int32_t)) { conn->state = CTX_READDATA; conn->readbufsize = ntohl( conn->readbufsize); if (conn->readbufsize > CONNECTION_MAXBUFSIZE) { log_error_conn( conn, "request message size out of range"); uv_close( (uv_handle_t*)handle, on_close); return; } conn->readbuf = (unsigned char*)malloc( conn->readbufsize); if (conn->readbuf == NULL) { log_error_conn( conn, "memory allocation error (request buffer)"); uv_close( (uv_handle_t*)handle, on_close); return; } } else { return; } /*no break here!*/ case CTX_READDATA: if (buf->len < nread) { nn = buf->len - bufidx; } else { nn = nread - bufidx; } if (nn > conn->readbufsize - conn->readbufpos) { log_error_conn( conn, "data size mismatch in request"); uv_close( (uv_handle_t*)handle, on_close); return; } memcpy( conn->readbuf + conn->readbufpos, (unsigned char*)buf->base + bufidx, nn); conn->readbufpos += nn; if (conn->readbufpos < conn->readbufsize) { /* ... have to read more */ return; } conn->state = CTX_PROCESS; uv_read_stop( handle); /*no break here!*/ case CTX_PROCESS: push_work_queue( conn); break; case CTX_TERMINATED: log_error_conn( conn, "got request data after termination"); uv_close( (uv_handle_t*)handle, on_close); break; } } else { if (nread == UV_EOF) { #ifdef STRUS_LOWLEVEL_DEBUG log_message_conn( conn, "got eof"); #endif conn->state = CTX_TERMINATED; uv_shutdown( &conn->shutdown_req, handle, on_shutdown); } else { log_error_conn_sys( conn, "disconnected", nread); uv_close( (uv_handle_t*)handle, on_close); } } } static void on_connected( uv_stream_t* stream, int status) { static int connection_id_cnt = 0; strus_connection_t* conn = 0; int res = 0; if (status != 0) { log_error_sys( "connection refused", status); return; } conn = (strus_connection_t*)calloc( 1, sizeof( strus_connection_t)); if (!conn) { log_error( "connection refused: out of memory"); goto ERROR_CLEANUP; } res = uv_tcp_init( stream->loop, &conn->tcp); if (res) { log_error_sys( "connection refused: init tcp context failed", res); goto ERROR_CLEANUP; } res = uv_accept( stream, (uv_stream_t*)&conn->tcp); if (res) { log_error_sys( "error in accept connection", res); goto ERROR_CLEANUP; } res = g_glbctx->init_handlerdata( &conn->handlerdata); if (res) { log_error( "error in initializing connection handler data"); goto ERROR_CLEANUP; } conn->id = ++connection_id_cnt; conn->tcp.data = conn; uv_read_start((uv_stream_t*)&conn->tcp, on_alloc, on_read); return; ERROR_CLEANUP: strus_free_connection( conn); } static void on_signal( uv_signal_t *handle, int signum) { const char* msg = "received signal unknown"; switch (signum) { case SIGINT: msg = "received signal SIGINT"; break; case SIGILL: msg = "received signal SIGILL"; break; case SIGABRT: msg = "received signal SIGABRT"; break; case SIGFPE: msg = "received signal SIGFPE"; break; case SIGSEGV: msg = "received signal SIGSEGV"; break; case SIGTERM: msg = "received signal SIGTERM"; break; case SIGHUP: msg = "received signal SIGHUP"; break; } if (signum == SIGHUP) { #ifdef STRUS_LOWLEVEL_DEBUG log_message( msg); #endif } else { log_message( msg); log_message( "shutting down server now ..."); uv_signal_stop( handle); uv_stop( g_server.loop); } } static void on_work( uv_work_t *req) { strus_connection_t* conn = (strus_connection_t*)( req->data); int err = 0; uint32_t msghdr; #ifdef STRUS_LOWLEVEL_DEBUG log_message_conn( conn, "started request"); strus_hexdump( g_glbctx->logf, "REQUEST", conn->readbuf, conn->readbufpos); #endif err = g_glbctx->request_handler( &conn->handlerdata, conn->readbuf, conn->readbufpos, sizeof(uint32_t), &conn->output, &conn->outputsize); if (err) { log_error_request( conn, (char*)conn->output); uv_close( (uv_handle_t*)&conn->tcp, on_close); return; } memset( &conn->write_req, 0, sizeof( conn->write_req)); conn->write_req.data = conn; conn->write_reqbuf.base = (char*)conn->output; conn->write_reqbuf.len = conn->outputsize; msghdr = htonl( conn->outputsize - sizeof( uint32_t)); memcpy( conn->write_reqbuf.base, &msghdr, sizeof( uint32_t)); }