Пример #1
0
/**
 * 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;
}
Пример #2
0
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
	}
}
Пример #3
0
/**
 * 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;
}
Пример #4
0
/**
 * 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;
}
Пример #5
0
/**
 * 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;
}
Пример #6
0
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));
}