[PATCH 4/5] session: introduce session_owner

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Mon Mar 19 16:34:51 MSK 2018


Session owner stores a session type specific data. For example,
IProto session has authentication salt, console session has
file descriptor.

For #2677 session owner of IProto and console will have push()
virtual function to do box.session.push, which implementation
depends on a session type.

Needed for #2677

Signed-off-by: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
---
 src/box/applier.cc        |  4 ++-
 src/box/authentication.cc |  4 +--
 src/box/authentication.h  |  3 +-
 src/box/box.cc            |  4 +--
 src/box/box.h             |  2 +-
 src/box/iproto.cc         | 86 +++++++++++++++++++++++++++++++++++++++------
 src/box/lua/session.c     | 73 +++++++++++++++++++++++++++++++++-----
 src/box/session.cc        | 72 +++++++++++++++++++++++++++++++++-----
 src/box/session.h         | 89 ++++++++++++++++++++++++++++++++++++++++-------
 src/box/vinyl.c           |  3 +-
 10 files changed, 293 insertions(+), 47 deletions(-)

diff --git a/src/box/applier.cc b/src/box/applier.cc
index 6bfe5a99a..581139509 100644
--- a/src/box/applier.cc
+++ b/src/box/applier.cc
@@ -533,7 +533,9 @@ applier_f(va_list ap)
 	 * Set correct session type for use in on_replace()
 	 * triggers.
 	 */
-	current_session()->type = SESSION_TYPE_APPLIER;
+	struct session_owner applier_owner;
+	session_owner_create(&applier_owner, SESSION_TYPE_APPLIER);
+	session_set_owner(current_session(), &applier_owner);
 
 	/* Re-connect loop */
 	while (!fiber_is_cancelled()) {
diff --git a/src/box/authentication.cc b/src/box/authentication.cc
index fef549c55..811974cb9 100644
--- a/src/box/authentication.cc
+++ b/src/box/authentication.cc
@@ -37,7 +37,7 @@
 static char zero_hash[SCRAMBLE_SIZE];
 
 void
-authenticate(const char *user_name, uint32_t len,
+authenticate(const char *user_name, uint32_t len, const char *salt,
 	     const char *tuple)
 {
 	struct user *user = user_find_by_name_xc(user_name, len);
@@ -84,7 +84,7 @@ authenticate(const char *user_name, uint32_t len,
 			   "invalid scramble size");
 	}
 
-	if (scramble_check(scramble, session->salt, user->def->hash2)) {
+	if (scramble_check(scramble, salt, user->def->hash2)) {
 		auth_res.is_authenticated = false;
 		if (session_run_on_auth_triggers(&auth_res) != 0)
 			diag_raise();
diff --git a/src/box/authentication.h b/src/box/authentication.h
index e91fe0a0e..9935e3548 100644
--- a/src/box/authentication.h
+++ b/src/box/authentication.h
@@ -45,6 +45,7 @@ struct on_auth_trigger_ctx {
 
 
 void
-authenticate(const char *user_name, uint32_t len, const char *tuple);
+authenticate(const char *user_name, uint32_t len, const char *salt,
+	     const char *tuple);
 
 #endif /* INCLUDES_TARANTOOL_BOX_AUTHENTICATION_H */
diff --git a/src/box/box.cc b/src/box/box.cc
index cb3199624..9fb85c04f 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1233,7 +1233,7 @@ box_on_join(const tt_uuid *instance_uuid)
 }
 
 void
-box_process_auth(struct auth_request *request)
+box_process_auth(struct auth_request *request, const char *salt)
 {
 	rmean_collect(rmean_box, IPROTO_AUTH, 1);
 
@@ -1243,7 +1243,7 @@ box_process_auth(struct auth_request *request)
 
 	const char *user = request->user_name;
 	uint32_t len = mp_decode_strl(&user);
-	authenticate(user, len, request->scramble);
+	authenticate(user, len, salt, request->scramble);
 }
 
 void
diff --git a/src/box/box.h b/src/box/box.h
index c9b5aad01..84899cc13 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -150,7 +150,7 @@ box_reset_stat(void);
 } /* extern "C" */
 
 void
-box_process_auth(struct auth_request *request);
+box_process_auth(struct auth_request *request, const char *salt);
 
 void
 box_process_join(struct ev_io *io, struct xrow_header *header);
diff --git a/src/box/iproto.cc b/src/box/iproto.cc
index a75127a90..ad4b1a757 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -59,6 +59,63 @@
 #include "replication.h" /* instance_uuid */
 #include "iproto_constants.h"
 #include "rmean.h"
+#include "random.h"
+
+enum { IPROTO_SALT_SIZE = 32 };
+
+/** Owner of binary IProto sessions. */
+struct iproto_session_owner {
+	struct session_owner base;
+	/** Authentication salt. */
+	char salt[IPROTO_SALT_SIZE];
+	/** IProto connection. */
+	struct iproto_connection *connection;
+};
+
+static struct session_owner *
+iproto_session_owner_dup(struct session_owner *owner);
+
+static int
+iproto_session_owner_fd(const struct session_owner *owner);
+
+static const struct session_owner_vtab iproto_session_owner_vtab = {
+	/* .dup = */ iproto_session_owner_dup,
+	/* .delete = */ (void (*)(struct session_owner *)) free,
+	/* .fd = */ iproto_session_owner_fd,
+};
+
+static struct session_owner *
+iproto_session_owner_dup(struct session_owner *owner)
+{
+	assert(owner->vtab == &iproto_session_owner_vtab);
+	size_t size = sizeof(struct iproto_session_owner);
+	struct session_owner *dup = (struct session_owner *) malloc(size);
+	if (dup == NULL) {
+		diag_set(OutOfMemory, size, "malloc", "iproto_session_owner");
+		return NULL;
+	}
+	memcpy(dup, owner, size);
+	return dup;
+}
+
+static inline void
+iproto_session_owner_create(struct iproto_session_owner *owner,
+			    struct iproto_connection *connection)
+{
+	owner->base.type = SESSION_TYPE_BINARY;
+	owner->base.vtab = &iproto_session_owner_vtab;
+	owner->connection = connection;
+	random_bytes(owner->salt, IPROTO_SALT_SIZE);
+}
+
+static inline char *
+iproto_session_salt(struct session *session)
+{
+	struct iproto_session_owner *session_owner =
+		(struct iproto_session_owner *) session->owner;
+	assert(session_owner->base.vtab == &iproto_session_owner_vtab);
+	return session_owner->salt;
+}
 
 /* The number of iproto messages in flight */
 enum { IPROTO_MSG_MAX = 768 };
@@ -368,6 +425,15 @@ struct iproto_connection
 static struct mempool iproto_connection_pool;
 static RLIST_HEAD(stopped_connections);
 
+static int
+iproto_session_owner_fd(const struct session_owner *owner)
+{
+	assert(owner->vtab == &iproto_session_owner_vtab);
+	const struct iproto_session_owner *session_owner =
+		((const struct iproto_session_owner *) owner);
+	return session_owner->connection->input.fd;
+}
+
 /**
  * Return true if we have not enough spare messages
  * in the message pool. Disconnect messages are
@@ -1033,11 +1099,6 @@ tx_process_disconnect(struct cmsg *m)
 	struct iproto_connection *con = msg->connection;
 	if (con->session) {
 		tx_fiber_init(con->session, 0);
-		/*
-		 * The socket is already closed in iproto thread,
-		 * prevent box.session.peer() from using it.
-		 */
-		con->session->fd = -1;
 		if (! rlist_empty(&session_on_disconnect))
 			session_run_on_disconnect_triggers(con->session);
 		session_destroy(con->session);
@@ -1328,8 +1389,9 @@ tx_process_misc(struct cmsg *m)
 {
 	struct iproto_msg *msg = tx_accept_msg(m);
 	struct obuf *out = msg->connection->tx.p_obuf;
+	struct session *session = msg->connection->session;
 
-	tx_fiber_init(msg->connection->session, msg->header.sync);
+	tx_fiber_init(session, msg->header.sync);
 
 	if (tx_check_schema(msg->header.schema_version))
 		goto error;
@@ -1337,7 +1399,8 @@ tx_process_misc(struct cmsg *m)
 	try {
 		switch (msg->header.type) {
 		case IPROTO_AUTH:
-			box_process_auth(&msg->auth);
+			box_process_auth(&msg->auth,
+					 iproto_session_salt(session));
 			iproto_reply_ok_xc(out, msg->header.sync,
 					   ::schema_version);
 			break;
@@ -1481,15 +1544,18 @@ tx_process_connect(struct cmsg *m)
 	struct iproto_connection *con = msg->connection;
 	struct obuf *out = msg->connection->tx.p_obuf;
 	try {              /* connect. */
-		con->session = session_create(con->input.fd, SESSION_TYPE_BINARY);
+		struct iproto_session_owner owner;
+		iproto_session_owner_create(&owner, con);
+		con->session = session_create((struct session_owner *) &owner);
 		if (con->session == NULL)
 			diag_raise();
 		tx_fiber_init(con->session, 0);
 		static __thread char greeting[IPROTO_GREETING_SIZE];
 		/* TODO: dirty read from tx thread */
 		struct tt_uuid uuid = INSTANCE_UUID;
-		greeting_encode(greeting, tarantool_version_id(),
-				&uuid, con->session->salt, SESSION_SEED_SIZE);
+		greeting_encode(greeting, tarantool_version_id(), &uuid,
+				iproto_session_salt(con->session),
+				IPROTO_SALT_SIZE);
 		obuf_dup_xc(out, greeting, IPROTO_GREETING_SIZE);
 		if (! rlist_empty(&session_on_connect)) {
 			if (session_run_on_connect_triggers(con->session) != 0)
diff --git a/src/box/lua/session.c b/src/box/lua/session.c
index d8e91bf1f..af8411068 100644
--- a/src/box/lua/session.c
+++ b/src/box/lua/session.c
@@ -42,6 +42,54 @@
 #include "box/user.h"
 #include "box/schema.h"
 
+/** Owner of a console session. */
+struct console_session_owner {
+	struct session_owner base;
+	/** Console socket descriptor. Expects text data. */
+	int fd;
+};
+
+static struct session_owner *
+console_session_owner_dup(struct session_owner *owner);
+
+static int
+console_session_owner_fd(const struct session_owner *owner);
+
+static const struct session_owner_vtab console_session_owner_vtab = {
+	/* .dup = */ console_session_owner_dup,
+	/* .delete = */ (void (*)(struct session_owner *)) free,
+	/* .fd = */ console_session_owner_fd,
+};
+
+static struct session_owner *
+console_session_owner_dup(struct session_owner *owner)
+{
+	assert(owner->vtab == &console_session_owner_vtab);
+	size_t size = sizeof(struct console_session_owner);
+	struct session_owner *dup = (struct session_owner *) malloc(size);
+	if (dup == NULL) {
+		diag_set(OutOfMemory, size, "malloc", "console_session_owner");
+		return NULL;
+	}
+	memcpy(dup, owner, size);
+	return dup;
+}
+
+static int
+console_session_owner_fd(const struct session_owner *owner)
+{
+	assert(owner->vtab == &console_session_owner_vtab);
+	return ((const struct console_session_owner *) owner)->fd;
+}
+
+static inline void
+console_session_owner_create(struct console_session_owner *owner, int fd)
+{
+	owner->base.type = SESSION_TYPE_CONSOLE;
+	owner->base.vtab = &console_session_owner_vtab;
+	owner->fd = fd;
+}
+
 static const char *sessionlib_name = "box.session";
 
 /* Create session and pin it to fiber */
@@ -56,14 +104,24 @@ lbox_session_create(struct lua_State *L)
 				     "session from Lua");
 	}
 	struct session *session = fiber_get_session(fiber());
+	int fd = luaL_optinteger(L, 1, -1);
 	if (session == NULL) {
-		int fd = luaL_optinteger(L, 1, -1);
-		session = session_create_on_demand(fd);
+		struct session_owner owner;
+		session_owner_create(&owner, type);
+		session = session_create_on_demand(&owner);
 		if (session == NULL)
 			return luaT_error(L);
 	}
-	/* If a session already exists, simply reset its type */
-	session->type = type;
+	/* If a session already exists, simply reset its owner. */
+	if (type == SESSION_TYPE_CONSOLE) {
+		struct console_session_owner owner;
+		console_session_owner_create(&owner, fd);
+		session_set_owner(session, (struct session_owner *) &owner);
+	} else {
+		struct session_owner owner;
+		session_owner_create(&owner, type);
+		session_set_owner(session, &owner);
+	}
 	lua_pushnumber(L, session->id);
 	return 1;
 }
@@ -90,7 +148,7 @@ lbox_session_id(struct lua_State *L)
 static int
 lbox_session_type(struct lua_State *L)
 {
-	lua_pushstring(L, session_type_strs[current_session()->type]);
+	lua_pushstring(L, session_type_strs[session_type(current_session())]);
 	return 1;
 }
 
@@ -237,7 +295,7 @@ lbox_session_fd(struct lua_State *L)
 	struct session *session = session_find(sid);
 	if (session == NULL)
 		luaL_error(L, "session.fd(): session does not exist");
-	lua_pushinteger(L, session->fd);
+	lua_pushinteger(L, session_fd(session));
 	return 1;
 }
 
@@ -251,7 +309,6 @@ lbox_session_peer(struct lua_State *L)
 	if (lua_gettop(L) > 1)
 		luaL_error(L, "session.peer(sid): bad arguments");
 
-	int fd;
 	struct session *session;
 	if (lua_gettop(L) == 1)
 		session = session_find(luaL_checkint(L, 1));
@@ -259,7 +316,7 @@ lbox_session_peer(struct lua_State *L)
 		session = current_session();
 	if (session == NULL)
 		luaL_error(L, "session.peer(): session does not exist");
-	fd = session->fd;
+	int fd = session_fd(session);
 	if (fd < 0) {
 		lua_pushnil(L); /* no associated peer */
 		return 1;
diff --git a/src/box/session.cc b/src/box/session.cc
index 0b0c5ae44..908ec9c4e 100644
--- a/src/box/session.cc
+++ b/src/box/session.cc
@@ -33,7 +33,6 @@
 #include "memory.h"
 #include "assoc.h"
 #include "trigger.h"
-#include "random.h"
 #include "user.h"
 #include "error.h"
 
@@ -54,6 +53,48 @@ RLIST_HEAD(session_on_connect);
 RLIST_HEAD(session_on_disconnect);
 RLIST_HEAD(session_on_auth);
 
+static struct session_owner *
+generic_session_owner_dup(struct session_owner *owner);
+
+static int
+generic_session_owner_fd(const struct session_owner *owner);
+
+static const struct session_owner_vtab generic_session_owner_vtab = {
+	/* .dup = */ generic_session_owner_dup,
+	/* .delete = */ (void (*)(struct session_owner *)) free,
+	/* .fd = */ generic_session_owner_fd,
+};
+
+static struct session_owner *
+generic_session_owner_dup(struct session_owner *owner)
+{
+	assert(owner->vtab == &generic_session_owner_vtab);
+	struct session_owner *dup =
+		(struct session_owner *) malloc(sizeof(*dup));
+	if (dup == NULL) {
+		diag_set(OutOfMemory, sizeof(*dup), "malloc",
+			 "default_session_owner");
+		return NULL;
+	}
+	memcpy(dup, owner, sizeof(*dup));
+	return dup;
+}
+
+static int
+generic_session_owner_fd(const struct session_owner *owner)
+{
+	assert(owner->vtab == &generic_session_owner_vtab);
+	(void) owner;
+	return -1;
+}
+
+void
+session_owner_create(struct session_owner *owner, enum session_type type)
+{
+	owner->type = type;
+	owner->vtab = &generic_session_owner_vtab;
+}
+
 static inline uint64_t
 sid_max()
 {
@@ -80,7 +121,7 @@ session_on_stop(struct trigger *trigger, void * /* event */)
 }
 
 struct session *
-session_create(int fd, enum session_type type)
+session_create(struct session_owner *owner)
 {
 	struct session *session =
 		(struct session *) mempool_alloc(&session_pool);
@@ -90,14 +131,15 @@ session_create(int fd, enum session_type type)
 		return NULL;
 	}
 	session->id = sid_max();
-	session->fd =  fd;
+	session->owner = session_owner_dup(owner);
+	if (session->owner == NULL) {
+		mempool_free(&session_pool, session);
+		return NULL;
+	}
 	session->sync = 0;
-	session->type = type;
 	/* For on_connect triggers. */
 	credentials_init(&session->credentials, guest_user->auth_token,
 			 guest_user->def->uid);
-	if (fd >= 0)
-		random_bytes(session->salt, SESSION_SEED_SIZE);
 	struct mh_i64ptr_node_t node;
 	node.key = session->id;
 	node.val = session;
@@ -105,6 +147,7 @@ session_create(int fd, enum session_type type)
 	mh_int_t k = mh_i64ptr_put(session_registry, &node, NULL, NULL);
 
 	if (k == mh_end(session_registry)) {
+		session_owner_delete(owner);
 		mempool_free(&session_pool, session);
 		diag_set(OutOfMemory, 0, "session hash", "new session");
 		return NULL;
@@ -112,13 +155,25 @@ session_create(int fd, enum session_type type)
 	return session;
 }
 
+int
+session_set_owner(struct session *session, struct session_owner *new_owner)
+{
+	struct session_owner *dup = session_owner_dup(new_owner);
+	if (dup == NULL)
+		return -1;
+	if (session->owner != NULL)
+		session_owner_delete(session->owner);
+	session->owner = dup;
+	return 0;
+}
+
 struct session *
-session_create_on_demand(int fd)
+session_create_on_demand(struct session_owner *owner)
 {
 	assert(fiber_get_session(fiber()) == NULL);
 
 	/* Create session on demand */
-	struct session *s = session_create(fd, SESSION_TYPE_BACKGROUND);
+	struct session *s = session_create(owner);
 	if (s == NULL)
 		return NULL;
 	s->fiber_on_stop = {
@@ -186,6 +241,7 @@ session_destroy(struct session *session)
 {
 	struct mh_i64ptr_node_t node = { session->id, NULL };
 	mh_i64ptr_remove(session_registry, &node, NULL);
+	session_owner_delete(session->owner);
 	mempool_free(&session_pool, session);
 }
 
diff --git a/src/box/session.h b/src/box/session.h
index 4f9235ea8..105dcab17 100644
--- a/src/box/session.h
+++ b/src/box/session.h
@@ -47,8 +47,6 @@ session_init();
 void
 session_free();
 
-enum {	SESSION_SEED_SIZE = 32, SESSION_DELIM_SIZE = 16 };
-
 enum session_type {
 	SESSION_TYPE_BACKGROUND = 0,
 	SESSION_TYPE_BINARY,
@@ -60,6 +58,49 @@ enum session_type {
 
 extern const char *session_type_strs[];
 
+struct session_owner_vtab;
+
+/**
+ * Object to store session type specific data. For example, IProto
+ * stores iproto_connection, console stores file descriptor.
+ */
+struct session_owner {
+	/** Session type. */
+	enum session_type type;
+	/** Virtual session owner methods. */
+	const struct session_owner_vtab *vtab;
+};
+
+struct session_owner_vtab {
+	/** Allocate a duplicate of an owner. */
+	struct session_owner *(*dup)(struct session_owner *);
+	/** Destroy an owner, and free its memory. */
+	void (*free)(struct session_owner *);
+	/** Get the descriptor of an owner, if has. Else -1. */
+	int (*fd)(const struct session_owner *);
+};
+
+static inline struct session_owner *
+session_owner_dup(struct session_owner *owner)
+{
+	return owner->vtab->dup(owner);
+}
+
+static inline void
+session_owner_delete(struct session_owner *owner)
+{
+	owner->vtab->free(owner);
+}
+
+/**
+ * Initialize a session owner with @a type and with default
+ * virtual methods.
+ * @param owner Session owner to initialize. Is copied inside.
+ * @param type Session type.
+ */
+void
+session_owner_create(struct session_owner *owner, enum session_type type);
+
 /**
  * Abstraction of a single user session:
  * for now, only provides accounting of established
@@ -72,10 +113,8 @@ extern const char *session_type_strs[];
 struct session {
 	/** Session id. */
 	uint64_t id;
-	/** File descriptor - socket of the connected peer.
-	 * Only if the session has a peer.
-	 */
-	int fd;
+	/** Session owner with type specific data. */
+	struct session_owner *owner;
 	/**
 	 * For iproto requests, we set this field
 	 * to the value of packet sync. Since the
@@ -85,15 +124,24 @@ struct session {
 	 * the first yield.
 	 */
 	uint64_t sync;
-	enum session_type type;
-	/** Authentication salt. */
-	char salt[SESSION_SEED_SIZE];
 	/** Session user id and global grants */
 	struct credentials credentials;
 	/** Trigger for fiber on_stop to cleanup created on-demand session */
 	struct trigger fiber_on_stop;
 };
 
+static inline enum session_type
+session_type(const struct session *session)
+{
+	return session->owner->type;
+}
+
+static inline int
+session_fd(const struct session *session)
+{
+	return session->owner->vtab->fd(session->owner);
+}
+
 /**
  * Find a session by id.
  */
@@ -154,7 +202,7 @@ extern struct credentials admin_credentials;
  * trigger to destroy it when this fiber ends.
  */
 struct session *
-session_create_on_demand(int fd);
+session_create_on_demand(struct session_owner *owner);
 
 /*
  * When creating a new fiber, the database (box)
@@ -171,7 +219,9 @@ current_session()
 {
 	struct session *session = fiber_get_session(fiber());
 	if (session == NULL) {
-		session = session_create_on_demand(-1);
+		struct session_owner owner;
+		session_owner_create(&owner, SESSION_TYPE_BACKGROUND);
+		session = session_create_on_demand(&owner);
 		if (session == NULL)
 			diag_raise();
 	}
@@ -191,7 +241,9 @@ effective_user()
 		(struct credentials *) fiber_get_key(fiber(),
 						     FIBER_KEY_USER);
 	if (u == NULL) {
-		session_create_on_demand(-1);
+		struct session_owner owner;
+		session_owner_create(&owner, SESSION_TYPE_BACKGROUND);
+		session_create_on_demand(&owner);
 		u = (struct credentials *) fiber_get_key(fiber(),
 							 FIBER_KEY_USER);
 	}
@@ -216,7 +268,18 @@ session_storage_cleanup(int sid);
  * trigger fails or runs out of resources.
  */
 struct session *
-session_create(int fd, enum session_type type);
+session_create(struct session_owner *owner);
+
+/**
+ * Set new owner of a session.
+ * @param session Session to change owner.
+ * @param new_owner New session owner. Is duplicated inside.
+ *
+ * @retval -1 Memory error.
+ * @retval  0 Success.
+ */
+int
+session_set_owner(struct session *session, struct session_owner *new_owner);
 
 /**
  * Destroy a session.
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index e0c30757c..7ae12f597 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -2453,7 +2453,8 @@ vinyl_engine_prepare(struct engine *engine, struct txn *txn)
 	 * available for the admin to track the lag so let the applier
 	 * wait as long as necessary for memory dump to complete.
 	 */
-	double timeout = (current_session()->type != SESSION_TYPE_APPLIER ?
+	double timeout = (session_type(current_session()) !=
+			  SESSION_TYPE_APPLIER ?
 			  env->timeout : TIMEOUT_INFINITY);
 	/*
 	 * Reserve quota needed by the transaction before allocating
-- 
2.14.3 (Apple Git-98)




More information about the Tarantool-patches mailing list