[tarantool-patches] [PATCH 4/5] session: introduce session_owner

  • From: Vladislav Shpilevoy <v.shpilevoy@xxxxxxxxxxxxx>
  • To: tarantool-patches@xxxxxxxxxxxxx
  • Date: Mon, 19 Mar 2018 16:34:51 +0300

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@xxxxxxxxxxxxx>
---
 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)


Other related posts: