diff --git a/doc/www/content/doc/box-protocol.rst b/doc/www/content/doc/box-protocol.rst index 95bb25dc2d40540285077ff48e33c920eac769e5..51677dd093bb672cf9c05e559a018378e66e5209 100644 --- a/doc/www/content/doc/box-protocol.rst +++ b/doc/www/content/doc/box-protocol.rst @@ -29,7 +29,7 @@ MsgPack data types: -* **MP_INT** - Unsigned Integer +* **MP_INT** - Integer * **MP_MAP** - Map * **MP_ARR** - Array * **MP_STRING** - String diff --git a/src/box/box.cc b/src/box/box.cc index c397d226556cd16d93abc6ee4c49742427f7f7f8..2371de06a21e51ad775f5a83f266b73c555ff723 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -52,13 +52,11 @@ #include "cfg.h" #include "iobuf.h" -static void process_ro(struct port *port, struct request *request); -static void process_rw(struct port *port, struct request *request); +static void process_ro(struct request *request, struct port *port); box_process_func box_process = process_ro; struct recovery_state *recovery; -static int stat_base; int snapshot_pid = 0; /* snapshot processes pid */ static void @@ -74,24 +72,11 @@ struct request_replace_body { } __attribute__((packed)); static void -process_rw(struct port *port, struct request *request) -{ - try { - stat_collect(stat_base, request->type, 1); - request->execute(request, port); - port_eof(port); - } catch (Exception *e) { - txn_rollback_stmt(); - throw; - } -} - -static void -process_ro(struct port *port, struct request *request) +process_ro(struct request *request, struct port *port) { if (!iproto_type_is_select(request->type)) tnt_raise(LoggedError, ER_READONLY); - return process_rw(port, request); + return process_rw(request, port); } void @@ -118,7 +103,7 @@ recover_row(struct recovery_state *r, void *param, struct xrow_header *row) request_decode(&request, (const char *) row->body[0].iov_base, row->body[0].iov_len); request.header = row; - process_rw(&null_port, &request); + process_rw(&request, &null_port); } /* {{{ configuration bindings */ @@ -249,7 +234,7 @@ box_leave_local_standby_mode(void *data __attribute__((unused))) */ space_end_recover(); - stat_cleanup(stat_base, IPROTO_TYPE_DML_MAX); + stat_cleanup(stat_base, IPROTO_TYPE_STAT_MAX); box_set_wal_mode(cfg_gets("wal_mode")); if (recovery_has_remote(recovery)) @@ -299,7 +284,7 @@ boxk(enum iproto_type type, uint32_t space_id, const char *format, ...) assert(data <= buf + sizeof(buf)); req.tuple = buf; req.tuple_end = data; - process_rw(&null_port, &req); + process_rw(&req, &null_port); } /** @@ -436,7 +421,7 @@ box_init() cfg_geti("panic_on_snap_error"), cfg_geti("panic_on_wal_error")); - stat_base = stat_register(iproto_type_strs, IPROTO_TYPE_DML_MAX); + stat_base = stat_register(iproto_type_strs, IPROTO_TYPE_STAT_MAX); if (recovery_has_data(recovery)) { /* Process existing snapshot */ diff --git a/src/box/box.h b/src/box/box.h index ca18d7043d8d6b4375751f376d9df1ba51fc5027..4729c99d10ed36f7f5d13affaf51c53b5431b102 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -63,7 +63,7 @@ box_atfork(); * change when entering/leaving read-only mode * (master->slave propagation). */ -typedef void (*box_process_func)(struct port *port, struct request *request); +typedef void (*box_process_func)(struct request *request, struct port *port); /** For read-write operations. */ extern box_process_func box_process; @@ -100,6 +100,9 @@ const char *box_status(void); void box_leave_local_standby_mode(void *data __attribute__((unused))); +void +box_process_auth(struct request *request); + void box_process_join(int fd, struct xrow_header *header); diff --git a/src/box/cluster.cc b/src/box/cluster.cc index 56490b4ce8f485683c1ed9c92bef38e746345f1d..fda7caac46d04d94cce76a98ca6d54821f295b92 100644 --- a/src/box/cluster.cc +++ b/src/box/cluster.cc @@ -53,7 +53,8 @@ cluster_set_server(const tt_uuid *server_uuid, uint32_t server_id) if (r->server_id == server_id) { if (tt_uuid_is_equal(&r->server_uuid, server_uuid)) return; - say_warn("server uuid changed to %s", tt_uuid_str(server_uuid)); + say_warn("server UUID changed to %s", + tt_uuid_str(server_uuid)); assert(vclock_has(&r->vclock, server_id)); memcpy(&r->server_uuid, server_uuid, sizeof(*server_uuid)); return; diff --git a/src/box/iproto.cc b/src/box/iproto.cc index c2b04236d9dff9a8d9d47821b2209efa34dffed9..814ad86818fe5fb7feafd740cf12a17739138947 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -46,6 +46,9 @@ #include "xrow.h" #include "iproto_constants.h" #include "user_def.h" +#include "authentication.h" +#include "stat.h" +#include "lua/call.h" /* {{{ iproto_request - declaration */ @@ -84,10 +87,7 @@ static void iproto_process_disconnect(struct iproto_request *request); static void -iproto_process_dml(struct iproto_request *request); - -static void -iproto_process_admin(struct iproto_request *request); +iproto_process(struct iproto_request *request); struct IprotoRequestGuard { struct iproto_request *ireq; @@ -471,7 +471,7 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in) if (reqend > in->end) break; struct iproto_request *ireq = - iproto_request_new(con, iproto_process_dml); + iproto_request_new(con, iproto_process); IprotoRequestGuard guard(ireq); xrow_header_decode(&ireq->header, &pos, reqend); @@ -482,7 +482,9 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in) * as well as in->pos must not be advanced, to * stay in sync. */ - if (iproto_type_is_dml(ireq->header.type)) { + if (ireq->header.type >= IPROTO_SELECT && + ireq->header.type <= IPROTO_AUTH) { + /* Pre-parse request before putting it into the queue */ if (ireq->header.bodycnt == 0) { tnt_raise(ClientError, ER_INVALID_MSGPACK, "request type"); @@ -491,8 +493,6 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in) pos = (const char *) ireq->header.body[0].iov_base; request_decode(&ireq->request, pos, ireq->header.body[0].iov_len); - } else { - ireq->process = iproto_process_admin; } ireq->request.header = &ireq->header; iproto_queue_push(&request_queue, guard.release()); @@ -630,45 +630,10 @@ iproto_connection_on_output(ev_loop *loop, struct ev_io *watcher, /* {{{ iproto_process_* functions */ static void -iproto_process_dml(struct iproto_request *ireq) +iproto_process(struct iproto_request *ireq) { struct iobuf *iobuf = ireq->iobuf; - struct iproto_connection *con = ireq->connection; - - auto scope_guard = make_scoped_guard([=]{ - /* Discard request (see iproto_enqueue_batch()) */ - iobuf->in.pos += ireq->total_len; - - if (evio_is_active(&con->output)) { - if (! ev_is_active(&con->output)) - ev_feed_event(con->loop, - &con->output, - EV_WRITE); - } else if (iproto_connection_is_idle(con)) { - iproto_connection_delete(con); - } - }); - - if (unlikely(! evio_is_active(&con->output))) - return; - struct obuf *out = &iobuf->out; - - struct iproto_port port; - iproto_port_init(&port, out, ireq->header.sync); - try { - box_process((struct port *) &port, &ireq->request); - } catch (Exception *e) { - if (port.found) - obuf_rollback_to_svp(out, &port.svp); - iproto_reply_error(out, e, ireq->header.sync); - } -} - -static void -iproto_process_admin(struct iproto_request *ireq) -{ - struct iobuf *iobuf = ireq->iobuf; struct iproto_connection *con = ireq->connection; auto scope_guard = make_scoped_guard([=]{ @@ -688,11 +653,33 @@ iproto_process_admin(struct iproto_request *ireq) if (unlikely(! evio_is_active(&con->output))) return; + struct obuf_svp svp = obuf_create_svp(out); try { switch (ireq->header.type) { + case IPROTO_SELECT: + case IPROTO_INSERT: + case IPROTO_REPLACE: + case IPROTO_UPDATE: + case IPROTO_DELETE: + struct iproto_port port; + iproto_port_init(&port, out, ireq->header.sync); + box_process(&ireq->request, (struct port *) &port); + break; + case IPROTO_CALL: + stat_collect(stat_base, ireq->request.type, 1); + box_lua_call(&ireq->request, &iobuf->out); + break; + case IPROTO_AUTH: + { + const char *user = ireq->request.key; + uint32_t len = mp_decode_strl(&user); + authenticate(user, len, ireq->request.tuple, + ireq->request.tuple_end); + iproto_reply_ok(&ireq->iobuf->out, ireq->header.sync); + break; + } case IPROTO_PING: - iproto_reply_ping(&ireq->iobuf->out, - ireq->header.sync); + iproto_reply_ok(&ireq->iobuf->out, ireq->header.sync); break; case IPROTO_JOIN: ev_io_stop(con->loop, &con->input); @@ -713,7 +700,7 @@ iproto_process_admin(struct iproto_request *ireq) (uint32_t) ireq->header.type); } } catch (Exception *e) { - say_error("admin command error: %s", e->errmsg()); + obuf_rollback_to_svp(&iobuf->out, &svp); iproto_reply_error(&iobuf->out, e, ireq->header.sync); } } diff --git a/src/box/iproto_constants.c b/src/box/iproto_constants.c index a0893501f7cf4bcb4e31acf23663fc7e05e1acb1..230454326d9df8f0866228558ca60f4fa1023d20 100644 --- a/src/box/iproto_constants.c +++ b/src/box/iproto_constants.c @@ -98,7 +98,7 @@ const char *iproto_type_strs[] = }; #define bit(c) (1ULL<<IPROTO_##c) -const uint64_t iproto_body_key_map[IPROTO_TYPE_DML_MAX] = { +const uint64_t iproto_body_key_map[IPROTO_AUTH + 1] = { 0, /* unused */ bit(SPACE_ID) | bit(LIMIT) | bit(KEY), /* SELECT */ bit(SPACE_ID) | bit(TUPLE), /* INSERT */ diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h index e89ad4fd3301e3912b8d8753503b00ef1dd833f7..917dbe62d72b0f3bfb5862e210f98dd2c7592093 100644 --- a/src/box/iproto_constants.h +++ b/src/box/iproto_constants.h @@ -116,9 +116,10 @@ enum iproto_type { IPROTO_REPLACE = 3, IPROTO_UPDATE = 4, IPROTO_DELETE = 5, + IPROTO_TYPE_DML_MAX = IPROTO_DELETE + 1, IPROTO_CALL = 6, + IPROTO_TYPE_STAT_MAX = IPROTO_CALL + 1, IPROTO_AUTH = 7, - IPROTO_TYPE_DML_MAX = IPROTO_AUTH + 1, /* admin command codes */ IPROTO_PING = 64, IPROTO_JOIN = 65, diff --git a/src/box/iproto_port.cc b/src/box/iproto_port.cc index d2810a08b550f478bdda7ab3b0dcfa7cf2f12858..8cfcb5bfd060460baded7829c46bdb2ab3870590 100644 --- a/src/box/iproto_port.cc +++ b/src/box/iproto_port.cc @@ -69,12 +69,14 @@ iproto_encode_error(uint32_t error) } void -iproto_reply_ping(struct obuf *out, uint64_t sync) +iproto_reply_ok(struct obuf *out, uint64_t sync) { struct iproto_header_bin reply = iproto_header_bin; - reply.v_len = mp_bswap_u32(sizeof(iproto_header_bin) - 5); + reply.v_len = mp_bswap_u32(sizeof(iproto_header_bin) - 5 + 1); reply.v_sync = mp_bswap_u64(sync); + uint8_t empty_map[1] = { 0x80 }; obuf_dup(out, &reply, sizeof(reply)); + obuf_dup(out, &empty_map, sizeof(empty_map)); } static inline uint32_t @@ -121,20 +123,32 @@ iproto_port_eof(struct port *ptr) struct iproto_port *port = iproto_port(ptr); /* found == 0 means add_tuple wasn't called at all. */ if (port->found == 0) { - port->svp = obuf_book(port->buf, SVP_SIZE); - port->size += SVP_SIZE; + port->svp = iproto_prepare_select(port->buf); } - uint32_t len = port->size - 5; + iproto_reply_select(port->buf, &port->svp, port->sync, port->found); +} + +struct obuf_svp +iproto_prepare_select(struct obuf *buf) +{ + return obuf_book(buf, SVP_SIZE); +} + +void +iproto_reply_select(struct obuf *buf, struct obuf_svp *svp, uint64_t sync, + uint32_t count) +{ + uint32_t len = obuf_size(buf) - svp->size - 5; struct iproto_header_bin header = iproto_header_bin; header.v_len = mp_bswap_u32(len); - header.v_sync = mp_bswap_u64(port->sync); + header.v_sync = mp_bswap_u64(sync); struct iproto_body_bin body = iproto_body_bin; - body.v_data_len = mp_bswap_u32(port->found); + body.v_data_len = mp_bswap_u32(count); - char *pos = (char *) obuf_svp_to_ptr(port->buf, &port->svp); + char *pos = (char *) obuf_svp_to_ptr(buf, svp); memcpy(pos, &header, sizeof(header)); memcpy(pos + sizeof(header), &body, sizeof(body)); } @@ -145,11 +159,9 @@ iproto_port_add_tuple(struct port *ptr, struct tuple *tuple) struct iproto_port *port = iproto_port(ptr); if (++port->found == 1) { /* Found the first tuple, add header. */ - port->svp = obuf_book(port->buf, SVP_SIZE); - port->size += SVP_SIZE; + port->svp = iproto_prepare_select(port->buf); } tuple_to_obuf(tuple, port->buf); - port->size += tuple->bsize; } struct port_vtab iproto_port_vtab = { diff --git a/src/box/iproto_port.h b/src/box/iproto_port.h index 9349f71c60c568605bc84ae191188d23ed0d8015..4233271ee2d2785943f10557d42a216688f8b9e3 100644 --- a/src/box/iproto_port.h +++ b/src/box/iproto_port.h @@ -64,7 +64,6 @@ struct iproto_port /** A pointer in the reply buffer where the reply starts. */ struct obuf_svp svp; /** Size of data written after reply starts */ - uint32_t size; }; extern struct port_vtab iproto_port_vtab; @@ -77,15 +76,21 @@ iproto_port_init(struct iproto_port *port, struct obuf *buf, port->buf = buf; port->sync = sync; port->found = 0; - port->size = 0; } /** Stack a reply to 'ping' packet. */ void -iproto_reply_ping(struct obuf *out, uint64_t sync); +iproto_reply_ok(struct obuf *out, uint64_t sync); /** Send an error packet back. */ void iproto_reply_error(struct obuf *out, const Exception *e, uint64_t sync); +struct obuf_svp +iproto_prepare_select(struct obuf *buf); + +void +iproto_reply_select(struct obuf *buf, struct obuf_svp *svp, uint64_t sync, + uint32_t count); + #endif /* TARANTOOL_IPROTO_PORT_H_INCLUDED */ diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 196eefdd85047893f2b49ee11865640739a62ba2..eb661774c1ad8e9c71f2948c7cf36df5dc6cac72 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -53,6 +53,7 @@ #include "box/schema.h" #include "box/session.h" #include "box/iproto_constants.h" +#include "box/iproto_port.h" /* contents of box.lua, misc.lua, box.net.lua respectively */ extern char session_lua[], @@ -147,60 +148,6 @@ port_lua_table_create(struct lua_State *L) return (struct port *) port; } -static void -port_add_lua_ret(struct port *port, struct lua_State *L, int index) -{ - struct tuple *tuple = lua_totuple(L, index, index); - TupleGuard guard(tuple); - port_add_tuple(port, tuple); -} - -/** - * Add all elements from Lua stack to fiber iov. - * - * To allow clients to understand a complex return from - * a procedure, we are compatible with SELECT protocol, - * and return the number of return values first, and - * then each return value as a tuple. - * - * If a Lua stack contains at least one scalar, each - * value on the stack is converted to a tuple. A Lua - * is converted to a tuple with multiple fields. - * - * If the stack is a Lua table, each member of which is - * not scalar, each member of the table is converted to - * a tuple. This way very large lists of return values can - * be used, since Lua stack size is limited by 8000 elements, - * while Lua table size is pretty much unlimited. - */ -static void -port_add_lua_multret(struct port *port, struct lua_State *L) -{ - int nargs = lua_gettop(L); - /** Check if we deal with a table of tables. */ - if (nargs == 1 && lua_istable(L, 1)) { - /* - * The table is not empty and consists of tables - * or tuples. Treat each table element as a tuple, - * and push it. - */ - lua_pushnil(L); - int has_keys = lua_next(L, 1); - if (has_keys && (lua_istable(L, -1) || lua_istuple(L, -1))) { - do { - port_add_lua_ret(port, L, lua_gettop(L)); - lua_pop(L, 1); - } while (lua_next(L, 1)); - return; - } else if (has_keys) { - lua_pop(L, 1); - } - } - for (int i = 1; i <= nargs; ++i) { - port_add_lua_ret(port, L, i); - } -} - /* }}} */ /** @@ -236,7 +183,7 @@ lbox_process(lua_State *L) struct request request; request_create(&request, op); request_decode(&request, req, sz); - box_process(port_lua, &request); + box_process(&request, port_lua); return 1; } @@ -319,7 +266,7 @@ boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id, request.key_end = key_end; try { - box_process(port, &request); + box_process(&request, port); return 0; } catch (Exception *e) { /* will be hanled by box.error() in Lua */ @@ -337,7 +284,7 @@ lbox_insert(lua_State *L) struct port_lua port; lbox_request_create(&request, L, IPROTO_INSERT, -1, 2); port_lua_create(&port, L); - box_process((struct port *) &port, &request); + box_process(&request, (struct port *) &port); return lua_gettop(L) - 2; } @@ -351,7 +298,7 @@ lbox_replace(lua_State *L) struct port_lua port; lbox_request_create(&request, L, IPROTO_REPLACE, -1, 2); port_lua_create(&port, L); - box_process((struct port *) &port, &request); + box_process(&request, (struct port *) &port); return lua_gettop(L) - 2; } @@ -367,7 +314,7 @@ lbox_update(lua_State *L) request.field_base = 1; /* field ids are one-indexed */ port_lua_create(&port, L); /* Ignore index_id for now */ - box_process((struct port *) &port, &request); + box_process(&request, (struct port *) &port); return lua_gettop(L) - 4; } @@ -382,7 +329,7 @@ lbox_delete(lua_State *L) lbox_request_create(&request, L, IPROTO_DELETE, 3, -1); port_lua_create(&port, L); /* Ignore index_id for now */ - box_process((struct port *) &port, &request); + box_process(&request, (struct port *) &port); return lua_gettop(L) - 3; } @@ -547,11 +494,9 @@ SetuidGuard::~SetuidGuard() * Invoke a Lua stored procedure from the binary protocol * (implementation of 'CALL' command code). */ -void -box_lua_call(struct request *request, struct port *port) +static inline void +execute_call(lua_State *L, struct request *request, struct obuf *out) { - lua_State *L = lua_newthread(tarantool_L); - LuarefGuard coro_ref(tarantool_L); const char *name = request->key; uint32_t name_len = mp_decode_strl(&name); @@ -573,9 +518,76 @@ box_lua_call(struct request *request, struct port *port) for (uint32_t i = 0; i < arg_count; i++) { luamp_decode(L, luaL_msgpack_default, &args); } - lbox_call(L, arg_count + oc - 1, LUA_MULTRET); - /* Send results of the called procedure to the client. */ - port_add_lua_multret(port, L); + lua_call(L, arg_count + oc - 1, LUA_MULTRET); + + /** + * Add all elements from Lua stack to iproto. + * + * To allow clients to understand a complex return from + * a procedure, we are compatible with SELECT protocol, + * and return the number of return values first, and + * then each return value as a tuple. + * + * If a Lua stack contains at least one scalar, each + * value on the stack is converted to a tuple. A Lua + * is converted to a tuple with multiple fields. + * + * If the stack is a Lua table, each member of which is + * not scalar, each member of the table is converted to + * a tuple. This way very large lists of return values can + * be used, since Lua stack size is limited by 8000 elements, + * while Lua table size is pretty much unlimited. + */ + + uint32_t count = 0; + struct obuf_svp svp = iproto_prepare_select(out); + + /** Check if we deal with a table of tables. */ + int nrets = lua_gettop(L); + if (nrets == 1 && lua_istable(L, 1)) { + /* + * The table is not empty and consists of tables + * or tuples. Treat each table element as a tuple, + * and push it. + */ + lua_pushnil(L); + int has_keys = lua_next(L, 1); + if (has_keys && (lua_istable(L, -1) || lua_istuple(L, -1))) { + do { + int top = lua_gettop(L); + luamp_encodestack(L, out, top, top); + ++count; + lua_pop(L, 1); + } while (lua_next(L, 1)); + goto done; + } else if (has_keys) { + lua_pop(L, 1); + } + } + for (int i = 1; i <= nrets; ++i) { + luamp_encodestack(L, out, i, i); + ++count; + } + +done: + iproto_reply_select(out, &svp, request->header->sync, count); +} + +void +box_lua_call(struct request *request, struct obuf *out) +{ + lua_State *L = NULL; + try { + L = lua_newthread(tarantool_L); + LuarefGuard coro_ref(tarantool_L); + execute_call(L, request, out); + } catch (Exception *e) { + /* Let all well-behaved exceptions pass through. */ + throw; + } catch (...) { + /* Convert Lua error to a Tarantool exception. */ + tnt_raise(LuajitError, L != NULL ? L : tarantool_L); + } } static int diff --git a/src/box/lua/call.h b/src/box/lua/call.h index 44ea45bd4a9abb987d4bdf666748ac8479dac0d6..bbb1a41f5a10613d5554b66a216ad2de24285996 100644 --- a/src/box/lua/call.h +++ b/src/box/lua/call.h @@ -39,7 +39,7 @@ struct port; * (implementation of 'CALL' command code). */ void -box_lua_call(struct request *request, struct port *port); +box_lua_call(struct request *request, struct obuf *out); extern "C" { struct port_ffi diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index fe16c3316b8f0ae6f5d811b2c91c773369c30171..17a980992b6a3e87d5ab2398a6606974fc51331f 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1085,7 +1085,7 @@ box.schema.user.drop = function(name) end box.schema.user.grant = function(user_name, privilege, object_type, - object_name, grantor) + object_name, options) -- From user point of view, role is the same thing -- as a privilege. Allow syntax grant(user, role). if object_name == nil and object_type == nil then @@ -1104,10 +1104,16 @@ box.schema.user.grant = function(user_name, privilege, object_type, end privilege_hex = privilege_resolve(privilege) local oid = object_resolve(object_type, object_name) - if grantor == nil then - grantor = session.uid() + if options == nil then + options = {} + end + if options.grantor == nil then + options.grantor = session.uid() else - grantor = user_or_role_resolve(grantor) + options.grantor = user_or_role_resolve(options.grantor) + end + if options.if_not_exists == nil then + options.if_not_exists = false end local _priv = box.space[box.schema.PRIV_ID] -- add the granted privilege to the current set @@ -1123,18 +1129,18 @@ box.schema.user.grant = function(user_name, privilege, object_type, -- XXX bug if we decide to add a grant option: new grantor -- replaces the old one, old grantor is lost if privilege_hex ~= old_privilege then - _priv:replace{grantor, uid, object_type, oid, privilege_hex} - else - if object_type == 'role' then - box.error(box.error.ROLE_GRANTED, user_name, object_name) - else - box.error(box.error.PRIV_GRANTED, user_name, privilege, - object_type, object_name) - end + _priv:replace{options.grantor, uid, object_type, oid, privilege_hex} + elseif options.if_not_exists == false then + if object_type == 'role' then + box.error(box.error.ROLE_GRANTED, user_name, object_name) + else + box.error(box.error.PRIV_GRANTED, user_name, privilege, + object_type, object_name) + end end end -box.schema.user.revoke = function(user_name, privilege, object_type, object_name) +box.schema.user.revoke = function(user_name, privilege, object_type, object_name, options) -- From user point of view, role is the same thing -- as a privilege. Allow syntax revoke(user, role). if object_name == nil and object_type == nil then @@ -1148,10 +1154,19 @@ box.schema.user.revoke = function(user_name, privilege, object_type, object_name if uid == nil then box.error(box.error.NO_SUCH_USER, name) end + if options == nil then + options = {} + end + if options.if_exists == nil then + options.if_exists = false + end local oid = object_resolve(object_type, object_name) local _priv = box.space[box.schema.PRIV_ID] local tuple = _priv:get{uid, object_type, oid} if tuple == nil then + if options.if_exists then + return + end if object_type == 'role' then box.error(box.error.ROLE_NOT_GRANTED, user_name, object_name) else diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h index 05a830b168ad46897ccd6fc49a28bc52235d8db2..6943d64fee5488f2cde5436e56183b3a8102faff 100644 --- a/src/box/lua/tuple.h +++ b/src/box/lua/tuple.h @@ -31,7 +31,6 @@ struct lua_State; struct txn; struct tuple; -struct tbuf; /** * Push tuple on lua stack @@ -46,7 +45,7 @@ struct tuple* lua_totuple(struct lua_State *L, int first, int last); int -luamp_encodestack(struct lua_State *L, struct tbuf *b, +luamp_encodestack(struct lua_State *L, struct obuf *b, int first, int last); void diff --git a/src/box/recovery.cc b/src/box/recovery.cc index b4077b17d9ea8ddc102a45c413c8564f73a28cc2..619f4b6e212b18e0cc89746b632b731df416019c 100644 --- a/src/box/recovery.cc +++ b/src/box/recovery.cc @@ -168,9 +168,9 @@ recovery_new(const char *snap_dirname, const char *wal_dirname, r->snapshot_handler = snapshot_handler; - xdir_create(&r->snap_dir, snap_dirname, SNAP); + xdir_create(&r->snap_dir, snap_dirname, SNAP, &r->server_uuid); - xdir_create(&r->wal_dir, wal_dirname, XLOG); + xdir_create(&r->wal_dir, wal_dirname, XLOG, &r->server_uuid); if (r->wal_mode == WAL_FSYNC) (void) strcat(r->wal_dir.open_wflags, "s"); @@ -181,7 +181,14 @@ recovery_new(const char *snap_dirname, const char *wal_dirname, try { xdir_scan(&r->snap_dir); - xdir_scan(&r->wal_dir); + /** + * Avoid scanning WAL dir before we recovered + * the snapshot and know server UUID - this will + * make sure the scan skips files with wrong + * UUID, see replication/cluster.test for + * details. + */ + xdir_check(&r->wal_dir); } catch (Exception *e) { e->log(); panic("can't scan a data directory"); @@ -265,10 +272,8 @@ recovery_bootstrap(struct recovery_state *r) const char *filename = "bootstrap.snap"; FILE *f = fmemopen((void *) &bootstrap_bin, sizeof(bootstrap_bin), "r"); - struct xlog *snap = xlog_open_stream(&r->snap_dir, - filename, NULL, NONE, f); - if (snap == NULL) - panic("failed to open %s", filename); + struct xlog *snap = xlog_open_stream(&r->snap_dir, 0, NONE, f, + filename); int rc = recover_xlog(r, snap); xlog_close(snap); @@ -300,12 +305,10 @@ recover_snap(struct recovery_state *r) if (res == NULL) panic("can't find snapshot"); sign = vclock_signature(res); - snap = xlog_open(&r->snap_dir, sign, NULL, NONE); - if (snap == NULL) - panic("can't open snapshot"); + snap = xlog_open(&r->snap_dir, sign, NONE); /* Save server uuid */ - memcpy(&r->server_uuid, &snap->server_uuid, sizeof(r->server_uuid)); + r->server_uuid = snap->server_uuid; /* Add a surrogate server id for snapshot rows */ vclock_add_server(&r->vclock, 0); @@ -314,7 +317,7 @@ recover_snap(struct recovery_state *r) if (recover_xlog(r, snap) != 0) panic("can't process snapshot"); - /* Replace server vclock using data from snapshot */ + /* Replace server vclock using the data from snapshot */ vclock_copy(&r->vclock, &snap->vclock); } @@ -414,13 +417,14 @@ recover_remaining_wals(struct recovery_state *r) * xlog_rename(). */ suffix = current_signt == last_signt ? INPROGRESS : NONE; - next_wal = xlog_open(&r->wal_dir, current_signt, - &r->server_uuid, suffix); - /* - * When doing final recovery, and dealing with the - * last file, try opening .<ext>.inprogress. - */ - if (next_wal == NULL) { + try { + next_wal = xlog_open(&r->wal_dir, current_signt, suffix); + } catch (XlogError *e) { + e->log(); + /* + * When doing final recovery, and dealing with the + * last file, try opening .<ext>.inprogress. + */ if (r->finalize && suffix == INPROGRESS) { /* * There is an .inprogress file, but @@ -936,7 +940,7 @@ wal_opt_rotate(struct xlog **wal, struct recovery_state *r, } if (l == NULL) { /* Open WAL with '.inprogress' suffix. */ - l = xlog_create(&r->wal_dir, &r->server_uuid, vclock); + l = xlog_create(&r->wal_dir, vclock); /* * Close the file *after* we create the new WAL, since * this is when replication relays get an inotify alarm @@ -1200,8 +1204,7 @@ void snapshot_save(struct recovery_state *r) { assert(r->snapshot_handler != NULL); - struct xlog *snap = xlog_create(&r->snap_dir, &r->server_uuid, - &r->vclock); + struct xlog *snap = xlog_create(&r->snap_dir, &r->vclock); if (snap == NULL) panic_status(errno, "Failed to save snapshot: failed to open file in write mode."); /* diff --git a/src/box/request.cc b/src/box/request.cc index e3289d6d58cf7f49d22cf55c8b6723aa1fbb0be7..8e1cb396fdf597aa1d6271f3e29aa47253065a41 100644 --- a/src/box/request.cc +++ b/src/box/request.cc @@ -38,10 +38,11 @@ #include <errinj.h> #include <fiber.h> #include <scoped_guard.h> -#include <third_party/base64.h> -#include "authentication.h" #include "user_def.h" #include "iproto_constants.h" +#include "stat.h" + +int stat_base; enum dup_replace_mode dup_replace_mode(uint32_t op) @@ -160,35 +161,41 @@ execute_select(struct request *request, struct port *port) } } +/** }}} */ + void -execute_auth(struct request *request, struct port * /* port */) +request_create(struct request *request, uint32_t type) { - const char *user = request->key; - uint32_t len = mp_decode_strl(&user); - authenticate(user, len, request->tuple, request->tuple_end); + + memset(request, 0, sizeof(*request)); + request->type = type; } -/** }}} */ +typedef void (*request_execute_f)(struct request *, struct port *); void -request_create(struct request *request, uint32_t type) +process_rw(struct request *request, struct port *port) { - if (!iproto_type_is_dml(type)) - tnt_raise(LoggedError, ER_UNKNOWN_REQUEST_TYPE, type); + assert(iproto_type_is_dml(request->type)); static const request_execute_f execute_map[] = { NULL, execute_select, execute_replace, execute_replace, - execute_update, execute_delete, box_lua_call, - execute_auth, + execute_update, execute_delete }; - memset(request, 0, sizeof(*request)); - request->type = type; - request->execute = execute_map[type]; + request_execute_f fun = execute_map[request->type]; + assert(fun != NULL); + stat_collect(stat_base, request->type, 1); + try { + fun(request, port); + port_eof(port); + } catch (Exception *e) { + txn_rollback_stmt(); + throw; + } } void request_decode(struct request *request, const char *data, uint32_t len) { - assert(request->execute != NULL); const char *end = data + len; uint64_t key_map = iproto_body_key_map[request->type]; diff --git a/src/box/request.h b/src/box/request.h index e257109f65fc3b07820e2d040831cc57b7c77b8c..b63f3dd74da04c797e85f2153dd55d09c1fcaab3 100644 --- a/src/box/request.h +++ b/src/box/request.h @@ -33,8 +33,7 @@ struct txn; struct port; - -typedef void (*request_execute_f)(struct request *, struct port *); +extern int stat_base; struct request { @@ -61,13 +60,14 @@ struct request const char *tuple_end; /** Base field offset for error messages, e.g. 0 for C and 1 for Lua. */ int field_base; - - request_execute_f execute; }; void request_create(struct request *request, uint32_t code); +void +process_rw(struct request *request, struct port *port); + void request_decode(struct request *request, const char *data, uint32_t len); diff --git a/src/box/xlog.cc b/src/box/xlog.cc index d9f033a14f26974093fa97a13222d6d001481ae0..452429475a935241b88a0cfc94f953e667e7b7ab 100644 --- a/src/box/xlog.cc +++ b/src/box/xlog.cc @@ -54,16 +54,27 @@ static const log_magic_t eof_marker = mp_bswap_u32(0xd510aded); /* host byte ord static const char inprogress_suffix[] = ".inprogress"; static const char v12[] = "0.12\n"; +XlogError::XlogError(const char *file, unsigned line, + const char *format, ...) + :Exception(file, line) +{ + va_list ap; + va_start(ap, format); + vsnprintf(m_errmsg, sizeof(m_errmsg), format, ap); + va_end(ap); +} + /* {{{ struct xdir */ void xdir_create(struct xdir *dir, const char *dirname, - enum xdir_type type) + enum xdir_type type, const tt_uuid *server_uuid) { memset(dir, 0, sizeof(*dir)); vclockset_new(&dir->index); /* Default mode. */ dir->mode = 0660; + dir->server_uuid = server_uuid; snprintf(dir->dirname, PATH_MAX, "%s", dirname); if (type == SNAP) { strcpy(dir->open_wflags, "wxd"); @@ -114,40 +125,39 @@ xdir_index_file(struct xdir *dir, int64_t signature) * The vclock stores the state of the log at the * time it is created. */ - struct xlog *wal = xlog_open(dir, signature, NULL, INPROGRESS); - if (wal == NULL) { - tnt_raise(ClientError, ER_INVALID_XLOG, - format_filename(dir, signature, NONE)); + struct xlog *wal; + + try { + wal = xlog_open(dir, signature, INPROGRESS); + /* + * All log files in a directory must satisfy Lamport's + * eventual order: events in each log file must be + * separable with consistent cuts, for example: + * + * log1: {1, 1, 0, 1}, log2: {1, 2, 0, 2} -- good + * log2: {1, 1, 0, 1}, log2: {2, 0, 2, 0} -- bad + */ + struct vclock *dup = vclockset_search(&dir->index, + &wal->vclock); + if (dup != NULL) { + XlogError *e = tnt_error(XlogError, + "%s: invalid xlog order", + wal->filename); + xlog_close(wal); + throw e; + } + } catch (XlogError *e) { + if (dir->panic_if_error) + throw; + /** Skip a corrupted file */ + e->log(); + return; } + auto log_guard = make_scoped_guard([=]{ xlog_close(wal); }); - /* - * Check the match between log file name and contents: - * the sum of vector clock coordinates must be the same - * as the name of the file. - */ - int64_t signature_check = vclock_signature(&wal->vclock); - if (signature_check != signature) { - tnt_raise(ClientError, ER_INVALID_XLOG_NAME, - (long long) signature_check, - (long long) signature); - } - /* - * All log files in a directory must satisfy Lamport's - * eventual order: events in each log file must be - * separable with consistent cuts, for example: - * - * log1: {1, 1, 0, 1}, log2: {1, 2, 0, 2} -- good - * log2: {1, 1, 0, 1}, log2: {2, 0, 2, 0} -- bad - */ - struct vclock *dup = vclockset_search(&dir->index, &wal->vclock); - if (dup != NULL) { - tnt_raise(ClientError, ER_INVALID_XLOG_ORDER, - (long long) signature, - (long long) vclock_signature(dup)); - } /* * Append the clock describing the file to the * directory index. @@ -212,7 +222,7 @@ xdir_scan(struct xdir *dir) dir->dirname); } - auto log_guard = make_scoped_guard([&]{ + auto dir_guard = make_scoped_guard([&]{ closedir(dh); free(signatures); }); @@ -278,7 +288,7 @@ xdir_scan(struct xdir *dir) signatures = (int64_t *) realloc(signatures, size); if (signatures == NULL) { tnt_raise(OutOfMemory, - size, "signatures array", "realloc"); + size, "realloc", "signatures array"); } } signatures[s_count++] = signature; @@ -304,15 +314,7 @@ xdir_scan(struct xdir *dir) vclock = next; } else if (s_old > s_new) { /** Add a new file. */ - try { - xdir_index_file(dir, s_new); - } catch (Exception *e) { - e->log(); - say_warn("failed to scan %s", - format_filename(dir, s_new, NONE)); - if (dir->panic_if_error) - throw; - } + xdir_index_file(dir, s_new); i++; } else { assert(s_old == s_new && i < s_count && @@ -323,6 +325,17 @@ xdir_scan(struct xdir *dir) } } +void +xdir_check(struct xdir *dir) +{ + DIR *dh = opendir(dir->dirname); /* log dir */ + if (dh == NULL) { + tnt_raise(SystemError, "error reading directory '%s'", + dir->dirname); + } + closedir(dh); +} + char * format_filename(struct xdir *dir, int64_t signature, enum log_suffix suffix) @@ -355,8 +368,11 @@ row_reader(FILE *f, struct xrow_header *row) if (feof(f)) return 1; error: - tnt_raise(ClientError, ER_INVALID_MSGPACK, - "invalid fixed header"); + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "%s: failed to read or parse row header" + " at offset %zu", fio_filename(fileno(f)), + (uint64_t) ftello(f)); + tnt_raise(ClientError, ER_INVALID_MSGPACK, buf); } /* Decode len, previous crc32 and row crc32 */ @@ -370,8 +386,11 @@ row_reader(FILE *f, struct xrow_header *row) goto error; uint32_t len = mp_decode_uint(&data); if (len > IPROTO_BODY_LEN_MAX) { - tnt_raise(ClientError, ER_INVALID_MSGPACK, - "packet is too big"); + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), + "%s: row is too big at offset %zu", + fio_filename(fileno(f)), (uint64_t) ftello(f)); + tnt_raise(ClientError, ER_INVALID_MSGPACK, buf); } /* Read previous crc32 */ @@ -394,8 +413,15 @@ row_reader(FILE *f, struct xrow_header *row) return 1; /* Validate checksum */ - if (crc32_calc(0, bodybuf, len) != crc32c) - tnt_raise(ClientError, ER_INVALID_MSGPACK, "invalid crc32"); + if (crc32_calc(0, bodybuf, len) != crc32c) { + char buf[PATH_MAX]; + + snprintf(buf, sizeof(buf), "%s: row checksum mismatch (expected %u)" + " at offset %zu", + fio_filename(fileno(f)), (unsigned) crc32c, + (uint64_t) ftello(f)); + tnt_raise(ClientError, ER_INVALID_MSGPACK, buf); + } data = bodybuf; xrow_header_decode(row, &data, bodybuf + len); @@ -459,9 +485,7 @@ xlog_cursor_close(struct xlog_cursor *i) * Seek back to last known good offset. */ fseeko(l->f, i->good_offset, SEEK_SET); -#if 0 region_free(&fiber()->gc); -#endif } /** @@ -483,14 +507,12 @@ xlog_cursor_next(struct xlog_cursor *i, struct xrow_header *row) say_debug("xlog_cursor_next: marker:0x%016X/%zu", row_marker, sizeof(row_marker)); -#if 0 /* * Don't let gc pool grow too much. Yet to * it before reading the next row, to make * sure it's not freed along here. */ region_free_after(&fiber()->gc, 128 * 1024); -#endif restart: if (marker_offset > 0) @@ -522,8 +544,7 @@ xlog_cursor_next(struct xlog_cursor *i, struct xrow_header *row) if (l->dir->panic_if_error) throw; else { - say_warn("failed to read row:"); - e->log(); + say_warn("failed to read row"); goto restart; } } @@ -538,8 +559,8 @@ xlog_cursor_next(struct xlog_cursor *i, struct xrow_header *row) eof: /* * The only two cases of fully read file: - * 1. sizeof(eof_marker) > 0 and it is the last record in file - * 2. sizeof(eof_marker) == 0 and there is no unread data in file + * 1. eof_marker is present and it is the last record in file + * 2. eof_marker is missing but there is no unread data in file */ if (ftello(l->f) == i->good_offset + sizeof(eof_marker)) { fseeko(l->f, i->good_offset, SEEK_SET); @@ -587,7 +608,7 @@ xlog_rename(struct xlog *l) new_filename[suffix - filename] = '\0'; if (rename(filename, new_filename) != 0) { - say_syserror("can't rename %s to %s", filename, new_filename); + say_syserror("%s: rename to %s failed", filename, new_filename); return -1; } @@ -608,7 +629,7 @@ inprogress_log_unlink(char *filename) if (errno == ENOENT) return 0; - say_syserror("can't unlink %s", filename); + say_syserror("%s: unlink() failed", filename); return -1; } @@ -634,12 +655,13 @@ xlog_close(struct xlog *l) if (! strchr(l->dir->open_wflags, 's')) xlog_sync(l); if (l->is_inprogress && xlog_rename(l) != 0) - panic("can't rename 'inprogress' WAL"); + panic("%s: rename() of 'inprogress' WAL failed", + l->filename); } r = fclose(l->f); if (r < 0) - say_syserror("can't close"); + say_syserror("%s: close() failed", l->filename); free(l); return r; } @@ -668,11 +690,13 @@ xlog_atfork(struct xlog **lptr) static int sync_cb(eio_req *req) { - if (req->result) - say_error("%s: fsync failed, errno: %d", - __func__, (int) req->result); - int fd = (intptr_t) req->data; + if (req->result) { + errno = req->result; + say_syserror("%s: fsync() failed", + fio_filename(fd)); + errno = 0; + } close(fd); return 0; } @@ -683,7 +707,7 @@ xlog_sync(struct xlog *l) if (l->dir->sync_is_async) { int fd = dup(fileno(l->f)); if (fd == -1) { - say_syserror("%s: dup() failed", __func__); + say_syserror("%s: dup() failed", l->filename); return -1; } eio_fsync(fd, 0, sync_cb, (void *) (intptr_t) fd); @@ -698,14 +722,13 @@ xlog_sync(struct xlog *l) #define VCLOCK_KEY "VClock" static int -xlog_write_meta(struct xlog *l, const tt_uuid *server_uuid, - const struct vclock *vclock) +xlog_write_meta(struct xlog *l) { char *vstr = NULL; if (fprintf(l->f, "%s%s", l->dir->filetype, v12) < 0 || fprintf(l->f, SERVER_UUID_KEY ": %s\n", - tt_uuid_str(server_uuid)) < 0 || - (vstr = vclock_to_string(vclock)) == NULL || + tt_uuid_str(l->dir->server_uuid)) < 0 || + (vstr = vclock_to_string(&l->vclock)) == NULL || fprintf(l->f, VCLOCK_KEY ": %s\n\n", vstr) < 0) { free(vstr); return -1; @@ -722,8 +745,8 @@ xlog_write_meta(struct xlog *l, const tt_uuid *server_uuid, * * @return 0 if success, -1 on error. */ -static int -xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid) +static void +xlog_read_meta(struct xlog *l, int64_t signature) { char filetype[32], version[32], buf[256]; struct xdir *dir = l->dir; @@ -731,23 +754,22 @@ xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid) if (fgets(filetype, sizeof(filetype), stream) == NULL || fgets(version, sizeof(version), stream) == NULL) { - say_error("%s: failed to read log file header", l->filename); - return -1; + tnt_raise(XlogError, "%s: failed to read log file header", + l->filename); } if (strcmp(dir->filetype, filetype) != 0) { - say_error("%s: unknown filetype", l->filename); - return -1; + tnt_raise(XlogError, "%s: unknown filetype", l->filename); } if (strcmp(v12, version) != 0) { - say_error("%s: unsupported file format version", l->filename); - return -1; + tnt_raise(XlogError, "%s: unsupported file format version", + l->filename); } for (;;) { if (fgets(buf, sizeof(buf), stream) == NULL) { - say_error("%s: failed to read log file header", + tnt_raise(XlogError, + "%s: failed to read log file header", l->filename); - return -1; } /** Empty line indicates the end of file header. */ if (strcmp(buf, "\n") == 0) @@ -760,8 +782,7 @@ xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid) char *key = buf; char *val = strchr(buf, ':'); if (val == NULL) { - say_error("%s: invalid meta", l->filename); - return -1; + tnt_raise(XlogError, "%s: invalid meta", l->filename); } *val++ = 0; while (isspace(*val)) @@ -770,86 +791,82 @@ xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid) if (strcmp(key, SERVER_UUID_KEY) == 0) { if ((end - val) != UUID_STR_LEN || tt_uuid_from_string(val, &l->server_uuid) != 0) { - say_error("%s: can't parse node uuid", + tnt_raise(XlogError, "%s: can't parse node UUID", l->filename); - return -1; } } else if (strcmp(key, VCLOCK_KEY) == 0){ size_t offset = vclock_from_string(&l->vclock, val); if (offset != 0) { - say_error("%s: invalid vclock at offset %zd", + tnt_raise(XlogError, "%s: invalid vclock at offset %zd", l->filename, offset); - return -1; } } else { /* Skip unknown key */ } } - if (server_uuid != NULL && !tt_uuid_is_nil(server_uuid) && - !tt_uuid_is_equal(server_uuid, &l->server_uuid)) { - say_error("%s: invalid server uuid", l->filename); - if (l->dir->panic_if_error) - return -1; + if (!tt_uuid_is_nil(dir->server_uuid) && + !tt_uuid_is_equal(dir->server_uuid, &l->server_uuid)) { + tnt_raise(XlogError, "%s: invalid server UUID", + l->filename); + } + /* + * Check the match between log file name and contents: + * the sum of vector clock coordinates must be the same + * as the name of the file. + */ + int64_t signature_check = vclock_signature(&l->vclock); + if (signature_check != signature) { + tnt_raise(XlogError, "%s: signature check failed", + l->filename); } - return 0; } struct xlog * -xlog_open_stream(struct xdir *dir, const char *filename, - const tt_uuid *server_uuid, enum log_suffix suffix, - FILE *file) +xlog_open_stream(struct xdir *dir, int64_t signature, enum log_suffix suffix, + FILE *file, const char *filename) { - struct xlog *l = NULL; - int save_errno; /* * Check fopen() result the caller first thing, to * preserve the errno. */ - if (file == NULL) { - save_errno = errno; - say_syserror("%s: failed to open file", filename); - goto error_1; - } - l = (struct xlog *) calloc(1, sizeof(*l)); - if (l == NULL) { - save_errno = errno; - say_syserror("%s: out of memory", filename); - goto error_2; - } + if (file == NULL) + tnt_raise(SystemError, "%s: failed to open file", filename); + + struct xlog *l = (struct xlog *) calloc(1, sizeof(*l)); + + auto log_guard = make_scoped_guard([=]{ + fclose(file); + free(l); + }); + + if (l == NULL) + tnt_raise(OutOfMemory, sizeof(*l), "malloc", "struct xlog"); + l->f = file; snprintf(l->filename, PATH_MAX, "%s", filename); l->mode = LOG_READ; l->dir = dir; l->is_inprogress = (suffix == INPROGRESS); vclock_create(&l->vclock); - if (xlog_read_meta(l, server_uuid) != 0) { - save_errno = EINVAL; - goto error_3; - } - return l; -error_3: - free(l); -error_2: - fclose(file); -error_1: - errno = save_errno; - return NULL; + xlog_read_meta(l, signature); + + log_guard.is_active = false; + return l; } struct xlog * -xlog_open(struct xdir *dir, int64_t signature, - const tt_uuid *server_uuid, enum log_suffix suffix) +xlog_open(struct xdir *dir, int64_t signature, enum log_suffix suffix) { const char *filename = format_filename(dir, signature, suffix); FILE *f = fopen(filename, "r"); - if (suffix == INPROGRESS && f == NULL) { - filename = format_filename(dir, signature, NONE); - f = fopen(filename, "r"); + if (f == NULL && suffix == INPROGRESS) { suffix = NONE; + filename = format_filename(dir, signature, suffix); + f = fopen(filename, "r"); } - return xlog_open_stream(dir, filename, server_uuid, suffix, f); + return xlog_open_stream(dir, signature, suffix, f, filename); } /** @@ -857,8 +874,7 @@ xlog_open(struct xdir *dir, int64_t signature, * and sets errno. */ struct xlog * -xlog_create(struct xdir *dir, const tt_uuid *server_uuid, - const struct vclock *vclock) +xlog_create(struct xdir *dir, const struct vclock *vclock) { char *filename; FILE *f = NULL; @@ -866,6 +882,7 @@ xlog_create(struct xdir *dir, const tt_uuid *server_uuid, int64_t signt = vclock_signature(vclock); assert(signt >= 0); + assert(!tt_uuid_is_nil(dir->server_uuid)); /* * Check whether a file with this name already exists. @@ -894,8 +911,9 @@ xlog_create(struct xdir *dir, const tt_uuid *server_uuid, l->mode = LOG_WRITE; l->dir = dir; l->is_inprogress = true; + vclock_copy(&l->vclock, vclock); setvbuf(l->f, NULL, _IONBF, 0); - if (xlog_write_meta(l, server_uuid, vclock) != 0) + if (xlog_write_meta(l) != 0) goto error; return l; @@ -906,6 +924,7 @@ xlog_create(struct xdir *dir, const tt_uuid *server_uuid, fclose(f); unlink(filename); /* try to remove incomplete file */ } + free(l); errno = save_errno; return NULL; } diff --git a/src/box/xlog.h b/src/box/xlog.h index 4ded0a2115647ba50641ac8e1f0e59f828a5cd38..df549ed8acd77833d611d8be07026c7c6d9bbc07 100644 --- a/src/box/xlog.h +++ b/src/box/xlog.h @@ -33,6 +33,18 @@ #include "tt_uuid.h" #include "vclock.h" +/** + * XlogError is raised when there is an error with contents + * of the data directory or a log file. A special subclass + * of exception is introduced to gracefully skip such errors + * in panic_if_error = false mode. + */ +struct XlogError: public Exception +{ + XlogError(const char *file, unsigned line, + const char *format, ...); +}; + /* {{{ log dir */ /** @@ -70,6 +82,14 @@ struct xdir { * O_DIRECT flag, for example. */ char open_wflags[6]; + /** + * A pointer to this server uuid. If not assigned + * (tt_uuid_is_nil returns true), server id check + * for logs in this directory is not performed. + * Otherwise, any log in this directory must have + * the matching server id. + */ + const struct tt_uuid *server_uuid; /** * Text of a marker written to the text file header: * XLOG (meaning it's a write ahead log) or SNAP (a @@ -97,8 +117,8 @@ struct xdir { * Initialize a log dir. */ void -xdir_create(struct xdir *dir, const char *dirname, - enum xdir_type type); +xdir_create(struct xdir *dir, const char *dirname, enum xdir_type type, + const struct tt_uuid *server_uuid); /** * Destroy a log dir object. @@ -115,6 +135,12 @@ xdir_destroy(struct xdir *dir); void xdir_scan(struct xdir *dir); +/** + * Check that a directory exists and is writable. + */ +void +xdir_check(struct xdir *dir); + /* }}} */ /** @@ -175,8 +201,6 @@ struct xlog { * * @param dir the log directory to look for a file * @param signature file name - * @param server_uuid the file must have been created by - * the server with this uuid, check it * @param suffix if IN_PROGRESS, look also for * .inprogress files * @@ -185,8 +209,8 @@ struct xlog { * Raises an exception in case of error. */ struct xlog * -xlog_open(struct xdir *dir, int64_t signature, - const tt_uuid *server_uuid, enum log_suffix suffix); +xlog_open(struct xdir *dir, int64_t signature, enum log_suffix suffix); + /** * Open an xlog from a pre-created stdio stream. The log * The log is open for reading. @@ -199,9 +223,8 @@ xlog_open(struct xdir *dir, int64_t signature, */ struct xlog * -xlog_open_stream(struct xdir *dir, const char *filename, - const tt_uuid *server_uuid, enum log_suffix suffix, - FILE *file); +xlog_open_stream(struct xdir *dir, int64_t signature, + enum log_suffix suffix, FILE *file, const char *filename); /** * Create a new file and open it in write (append) mode. @@ -212,11 +235,11 @@ xlog_open_stream(struct xdir *dir, const char *filename, * @param vclock the global state of replication (vector * clock) at the moment the file is created. * - * @return xlog object. Raises an exception in case of error. + * @return xlog object or NULL in case of error. */ struct xlog * -xlog_create(struct xdir *dir, const tt_uuid *server_uuid, - const struct vclock *vclock); +xlog_create(struct xdir *dir, const struct vclock *vclock); + /** * Sync a log file. The exact action is defined * by xdir flags. diff --git a/src/coio.cc b/src/coio.cc index e2903365f1f0cb0c18aa77eda240d63fdf902002..282b3db407718a6764347a79f3a2416fe3e47cf9 100644 --- a/src/coio.cc +++ b/src/coio.cc @@ -44,11 +44,8 @@ struct CoioGuard { ~CoioGuard() { ev_io_stop(loop(), ev_io); } }; -static inline void -fiber_schedule_coio(ev_loop * /* loop */, ev_io *watcher, int event) -{ - return fiber_schedule((ev_watcher *) watcher, event); -} +typedef void (*ev_io_cb)(ev_loop *, ev_io *, int); +typedef void (*ev_stat_cb)(ev_loop *, ev_stat *, int); /** Note: this function does not throw */ void @@ -56,13 +53,18 @@ coio_init(struct ev_io *coio) { /* Prepare for ev events. */ coio->data = fiber(); - ev_init(coio, fiber_schedule_coio); + ev_init(coio, (ev_io_cb) fiber_schedule); coio->fd = -1; } static inline void coio_fiber_yield(struct ev_io *coio) { + /** + * We may create an event in one fiber, but wait for it + * in another. Hence we set the coroutine right before the + * yield. + */ coio->data = fiber(); fiber_yield(); #ifdef DEBUG @@ -607,3 +609,21 @@ coio_service_init(struct coio_service *service, const char *name, service->handler = handler; service->handler_param = handler_param; } + +void +coio_stat_init(ev_stat *stat, const char *path) +{ + ev_stat_init(stat, (ev_stat_cb) fiber_schedule, path, 0.0); +} + +void +coio_stat_stat_timeout(ev_stat *stat, ev_tstamp timeout) +{ + stat->data = fiber(); + ev_stat_start(loop(), stat); + ev_tstamp start, delay; + coio_timeout_init(&start, &delay, timeout); + fiber_yield_timeout(delay); + ev_stat_stop(loop(), stat); + fiber_testcancel(); +} diff --git a/src/coio.h b/src/coio.h index 876778a44cc54e321f6ae5e9210ac7acafe993d7..25bf23ede9a8a72877c80187a45f989a5d99d879 100644 --- a/src/coio.h +++ b/src/coio.h @@ -149,4 +149,11 @@ coio_service_init(struct coio_service *service, const char *name, const char *uri, void (*handler)(va_list ap), void *handler_param); + +void +coio_stat_init(ev_stat *stat, const char *path); + +void +coio_stat_stat_timeout(ev_stat *stat, ev_tstamp delay); + #endif /* TARANTOOL_COIO_H_INCLUDED */ diff --git a/src/exception.cc b/src/exception.cc index 0a19f9af1d92ee31831c816963dbba7c273a8019..ca2080928224a9212adf011d6fbeae1df0889714 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -112,11 +112,20 @@ Exception::Exception(const Exception& e) memcpy(m_errmsg, e.m_errmsg, sizeof(m_errmsg)); } +/** A quick & dirty version of name demangle for class names */ +static const char * +demangle(const char *name) +{ + char *res = NULL; + (void) strtol(name, &res, 10); + return res && strlen(res) ? res : name; +} + void Exception::log() const { - _say(S_ERROR, m_file, m_line, "%s %s", - typeid(*this).name(), m_errmsg); + _say(S_ERROR, m_file, m_line, m_errmsg, "%s", + demangle(typeid(*this).name())); } diff --git a/src/exception.h b/src/exception.h index 1de31cf161c6aa589f77bacb07619859109b6fe2..c2d68033fbed1679cb2979723ea8eb6a9e77b5b1 100644 --- a/src/exception.h +++ b/src/exception.h @@ -113,10 +113,13 @@ class OutOfMemory: public SystemError { const char *object); }; -#define tnt_raise(...) tnt_raise0(__VA_ARGS__) -#define tnt_raise0(class, ...) do { \ +#define tnt_error(class, ...) ({ \ say_debug("%s at %s:%i", #class, __FILE__, __LINE__); \ - throw new class(__FILE__, __LINE__, ##__VA_ARGS__); \ + new class(__FILE__, __LINE__, ##__VA_ARGS__); \ +}) + +#define tnt_raise(...) do { \ + throw tnt_error(__VA_ARGS__); \ } while (0) #endif /* TARANTOOL_EXCEPTION_H_INCLUDED */ diff --git a/src/ffisyms.cc b/src/ffisyms.cc index e0d34d7703811759ade585ed49df800b980dd77f..3e9f0f65b1791ff1db19fbca06b0f99938bb99ce 100644 --- a/src/ffisyms.cc +++ b/src/ffisyms.cc @@ -11,6 +11,7 @@ #include "lua/bsdsocket.h" #include "lua/digest.h" #include "base64.h" +#include "random.h" #include <lib/salad/guava.h> /* @@ -52,5 +53,6 @@ void *ffi_symbols[] = { (void *) base64_encode, (void *) base64_bufsize, (void *) SHA1internal, - (void *) guava + (void *) guava, + (void *) random_bytes, }; diff --git a/src/fiber.cc b/src/fiber.cc index b6f7109b531cd75253ea50c260e876019934b3f2..7754cedc49eedec7feb842d366486a7860d826dc 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -273,22 +273,19 @@ fiber_sleep(ev_tstamp delay) fiber_testcancel(); } +typedef void (*ev_child_cb)(ev_loop *, ev_child *, int); + /** Wait for a forked child to complete. * @note: this is a cancellation point (@sa fiber_testcancel()). * @return process return status */ -void -fiber_schedule_child(ev_loop * /* loop */, ev_child *watcher, int event) -{ - return fiber_schedule((ev_watcher *) watcher, event); -} int wait_for_child(pid_t pid) { assert(cord() == &main_cord); ev_child cw; - ev_init(&cw, fiber_schedule_child); + ev_init(&cw, (ev_child_cb) fiber_schedule); ev_child_set(&cw, pid, 0); cw.data = fiber(); ev_child_start(loop(), &cw); @@ -300,7 +297,7 @@ wait_for_child(pid_t pid) } void -fiber_schedule(ev_watcher *watcher, int event __attribute__((unused))) +fiber_schedule(ev_loop * /* loop */, ev_watcher *watcher, int /* revents */) { assert(fiber() == &cord()->sched); fiber_call((struct fiber *) watcher->data); diff --git a/src/fiber.h b/src/fiber.h index 3f72521aef10fafcc2ffb71c61301faed0bf69c5..43bd981663fe8043003aa67c92ea10a6b8f77b62 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -261,8 +261,9 @@ void fiber_testcancel(void); */ bool fiber_setcancellable(bool enable); void fiber_sleep(ev_tstamp s); -struct tbuf; -void fiber_schedule(ev_watcher *watcher, int event __attribute__((unused))); + +void +fiber_schedule(ev_loop * /* loop */, ev_watcher *watcher, int revents); /** * \brief Associate \a value with \a key in fiber local storage @@ -277,6 +278,9 @@ fiber_set_key(struct fiber *fiber, enum fiber_key key, void *value) fiber->fls[key] = value; } +bool +fiber_is_cancelled(); + /** * \brief Retrieve value by \a key from fiber local storage * \param fiber fiber diff --git a/src/fio.c b/src/fio.c index 7f1024bd8e1a1cf9b269e95894d654fc422a30e1..2edae2f054e3663ab70f8e52dbea0ee5406e6efe 100644 --- a/src/fio.c +++ b/src/fio.c @@ -40,7 +40,7 @@ #include <say.h> -static const char * +const char * fio_filename(int fd) { #ifdef TARGET_OS_LINUX diff --git a/src/fio.h b/src/fio.h index 957c8273a5097202f6044b84dca382341251dfbb..db7a59362c0118aca3b8cc3bfd6999a7ba662548 100644 --- a/src/fio.h +++ b/src/fio.h @@ -41,6 +41,9 @@ extern "C" { #endif /* defined(__cplusplus) */ +const char * +fio_filename(int fd); + struct iovec; /** * Read up to N bytes from file into the buffer, diff --git a/src/lua/digest.lua b/src/lua/digest.lua index 724bfb24d7825ae9c86009c879495f59f9c4f75d..96a86e3d386e9b35223bd779aced21b522be3b91 100644 --- a/src/lua/digest.lua +++ b/src/lua/digest.lua @@ -30,6 +30,9 @@ ffi.cdef[[ int base64_bufsize(int binsize); int base64_decode(const char *in_base64, int in_len, char *out_bin, int out_len); int base64_encode(const char *in_bin, int in_len, char *out_base64, int out_len); + + /* random */ + void random_bytes(char *, size_t); ]] local ssl @@ -135,6 +138,15 @@ local m = { guava = function(state, buckets) return ffi.C.guava(state, buckets) end, + + urandom = function(n) + if n == nil then + error('Usage: digest.urandom(len)') + end + local buf = ffi.new('char[?]', n) + ffi.C.random_bytes(buf, n) + return ffi.string(buf, n) + end } if ssl ~= nil then diff --git a/src/lua/log.lua b/src/lua/log.lua index 8b37eab52196a980c223529b21f05e1acab8e19b..fdad8836637dea08104a4d4c3e40e055eff822a1 100644 --- a/src/lua/log.lua +++ b/src/lua/log.lua @@ -20,16 +20,7 @@ ffi.cdef[[ ]] local function say(level, fmt, ...) - local str - - args = { ... } - if not pcall(function() str = string.format(fmt, unpack(args)) end) then - str = fmt .. ' [' - for i = 1, select('#', ...) do - str = str .. select(i, ...) .. ', ' - end - str = str .. ']' - end + local str = string.format(fmt, ...) local frame = debug.getinfo(3, "Sl") local line = 0 local file = 'eval' diff --git a/test/app/trigger_atexit.result b/test/app/trigger_atexit.result index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..19f86f493ab110b8dc8279a024880e44203968d8 100644 --- a/test/app/trigger_atexit.result +++ b/test/app/trigger_atexit.result @@ -0,0 +1 @@ +done diff --git a/test/app/trigger_atexit.test.lua b/test/app/trigger_atexit.test.lua index 7ac54bd5e961396560ef15d4637ce6c8110205d6..a6044ec3975c6765df4d8f7d031fc018fa950dae 100755 --- a/test/app/trigger_atexit.test.lua +++ b/test/app/trigger_atexit.test.lua @@ -27,10 +27,11 @@ box.space.abc:create_index('pk', { type = 'tree' }) box.space.abc:on_replace(test_replace) -cleanup_list = fio.glob(fio.pathjoin(tempdir), '*') +cleanup_list = fio.glob(fio.pathjoin(tempdir, '*')) for _, file in pairs(cleanup_list) do fio.unlink(file) end fio.rmdir(tempdir) +print("done") os.exit(0) diff --git a/test/box/access.result b/test/box/access.result index bef0e0c8cde03382829e5c70fca542a2212db376..6ba4f59a0b572eceb6fc23d006753c7e574a0b0f 100644 --- a/test/box/access.result +++ b/test/box/access.result @@ -529,3 +529,24 @@ box.schema.role.exists('public') --- - true ... +-- test if_exists/if_not_exists in grant/revoke +box.schema.user.grant('guest', 'read,write,execute', 'universe') +--- +... +box.schema.user.grant('guest', 'read,write,execute', 'universe') +--- +- error: User 'guest' already has read,write,execute access on universe 'nil' +... +box.schema.user.grant('guest', 'read,write,execute', 'universe', '', { if_not_exists = true }) +--- +... +box.schema.user.revoke('guest', 'read,write,execute', 'universe') +--- +... +box.schema.user.revoke('guest', 'read,write,execute', 'universe') +--- +- error: User 'guest' does not have read,write,execute access on universe 'nil' +... +box.schema.user.revoke('guest', 'read,write,execute', 'universe', '', { if_exists = true }) +--- +... diff --git a/test/box/access.test.lua b/test/box/access.test.lua index 9c39abe7f58151b98afad79e610dde3332b32b55..41fe522ed98f820a6d133c7ed708cb8fc7527935 100644 --- a/test/box/access.test.lua +++ b/test/box/access.test.lua @@ -212,3 +212,10 @@ box.schema.func.exists('box.schema.user.info') -- gh-665: user.exists() should nto be true for roles box.schema.user.exists('public') box.schema.role.exists('public') +-- test if_exists/if_not_exists in grant/revoke +box.schema.user.grant('guest', 'read,write,execute', 'universe') +box.schema.user.grant('guest', 'read,write,execute', 'universe') +box.schema.user.grant('guest', 'read,write,execute', 'universe', '', { if_not_exists = true }) +box.schema.user.revoke('guest', 'read,write,execute', 'universe') +box.schema.user.revoke('guest', 'read,write,execute', 'universe') +box.schema.user.revoke('guest', 'read,write,execute', 'universe', '', { if_exists = true }) diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result index 5ef12fd2ebb1399b96800390a8c81b2d9b029a4f..691d63bd6030755bf6f0b26ac5171952aa40fb2d 100644 --- a/test/box/bsdsocket.result +++ b/test/box/bsdsocket.result @@ -168,11 +168,11 @@ pong = s:sysread() ... string.len(pong) --- -- 22 +- 23 ... msgpack.decode(pong) --- -- 17 +- 18 - 6 ... msgpack.decode(pong, 6) diff --git a/test/box/digest.result b/test/box/digest.result index f290ccc0823c6ee878a1dc2859630557c0922cc0..a087ba03bc84b072e1645cd7ea5c2c2f7676fc69 100644 --- a/test/box/digest.result +++ b/test/box/digest.result @@ -193,19 +193,19 @@ digest.base64_decode(b) == s ... digest.base64_decode(nil) --- -- error: 'builtin/digest.lua:88: Usage: digest.base64_decode(string)' +- error: 'builtin/digest.lua:91: Usage: digest.base64_decode(string)' ... digest.base64_encode(nil) --- -- error: 'builtin/digest.lua:77: Usage: digest.base64_encode(string)' +- error: 'builtin/digest.lua:80: Usage: digest.base64_encode(string)' ... digest.base64_encode(123) --- -- error: 'builtin/digest.lua:77: Usage: digest.base64_encode(string)' +- error: 'builtin/digest.lua:80: Usage: digest.base64_encode(string)' ... digest.base64_decode(123) --- -- error: 'builtin/digest.lua:88: Usage: digest.base64_decode(string)' +- error: 'builtin/digest.lua:91: Usage: digest.base64_decode(string)' ... digest.guava('hello', 0) --- @@ -227,6 +227,22 @@ digest.guava(1673758223894951030, 11) --- - 7 ... +digest.urandom() +--- +- error: 'builtin/digest.lua:144: Usage: digest.urandom(len)' +... +#digest.urandom(0) +--- +- 0 +... +#digest.urandom(1) +--- +- 1 +... +#digest.urandom(16) +--- +- 16 +... digest = nil --- ... diff --git a/test/box/digest.test.lua b/test/box/digest.test.lua index a4583eece4f2fb356fd3df99330086a35c11c338..5c1b8b00f22f1000802b36c3fa8eeeda11cb0959 100644 --- a/test/box/digest.test.lua +++ b/test/box/digest.test.lua @@ -65,4 +65,9 @@ digest.guava(10863919174838991, 11) digest.guava(2016238256797177309, 11) digest.guava(1673758223894951030, 11) +digest.urandom() +#digest.urandom(0) +#digest.urandom(1) +#digest.urandom(16) + digest = nil diff --git a/test/box/misc.result b/test/box/misc.result index 4a6b2751e572cbe622e672e66348f3227db44573..fd4be8cb229ab86d67ac48a1041754790c504888 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -88,7 +88,6 @@ t; - SELECT - REPLACE - INSERT - - AUTH - CALL - UPDATE - total diff --git a/test/lib/unittest_server.py b/test/lib/unittest_server.py index a700aa2da1ff827d857557a3d8d763f832ae47f1..708a34304b6479ba04b96bd9eb143bb57d4bc567 100644 --- a/test/lib/unittest_server.py +++ b/test/lib/unittest_server.py @@ -4,7 +4,7 @@ import sys import glob import traceback import subprocess -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, STDOUT from lib.server import Server from lib.tarantool_server import Test @@ -12,7 +12,7 @@ from lib.tarantool_server import Test class UnitTest(Test): def execute(self, server): execs = [os.path.join(server.builddir, "test", self.name)] - proc = Popen(execs, stdout=PIPE) + proc = Popen(execs, stdout=PIPE, stderr=STDOUT) sys.stdout.write(proc.communicate()[0]) class UnittestServer(Server): diff --git a/test/replication/cluster.result b/test/replication/cluster.result index 520f03d4ea42fee624cab79f1a88296757456c82..230cd06a13e0bcc6a4a72297c3d7a0ca53354182 100644 --- a/test/replication/cluster.result +++ b/test/replication/cluster.result @@ -9,9 +9,9 @@ box.info.server.uuid --- - 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae ... -check log line for 'server uuid changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' +check log line for 'server UUID changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' -'server uuid changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' exists in server log +'server UUID changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' exists in server log box.info.server.uuid --- diff --git a/test/replication/cluster.test.py b/test/replication/cluster.test.py index 11ef2c1a4c7ada947c0c4c148151b762ff3ee775..6abd7af917b542b2c34fa5140183d4b939577204 100644 --- a/test/replication/cluster.test.py +++ b/test/replication/cluster.test.py @@ -18,7 +18,7 @@ server.admin("box.info.server.uuid") server.stop() server.start() -line = "server uuid changed to " + new_uuid +line = "server UUID changed to " + new_uuid print "check log line for '%s'" % line print if server.logfile_pos.seek_once(line) >= 0: diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 1091ee26dd8b55c979b9d6c98b7e40eec19172a9..c15d0861ab701aebcd96d20a6437834b30463572 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -6,9 +6,9 @@ include_directories(${PROJECT_SOURCE_DIR}/src) include_directories(${PROJECT_BINARY_DIR}/src) include_directories(${PROJECT_SOURCE_DIR}/src/lib) include_directories(${CMAKE_SOURCE_DIR}/third_party) -add_executable(rlist.test rlist.c test.c ${CMAKE_SOURCE_DIR}/src/lib/salad/rlist.c) -add_executable(uri.test uri.c test.c ${CMAKE_SOURCE_DIR}/src/uri.c) -add_executable(fiob.test test.c fiob.c ${CMAKE_SOURCE_DIR}/src/fiob.c) +add_executable(rlist.test rlist.c unit.c ${CMAKE_SOURCE_DIR}/src/lib/salad/rlist.c) +add_executable(uri.test uri.c unit.c ${CMAKE_SOURCE_DIR}/src/uri.c) +add_executable(fiob.test unit.c fiob.c ${CMAKE_SOURCE_DIR}/src/fiob.c) add_executable(queue.test queue.c) add_executable(mhash.test mhash.c) add_executable(mhash_bytemap.test mhash_bytemap.c) @@ -48,17 +48,27 @@ add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtr target_link_libraries(rtree_itr.test) add_executable(matras.test matras.cc) target_link_libraries(matras.test small) -add_executable(vclock.test vclock.cc test.c +add_executable(vclock.test vclock.cc unit.c ${CMAKE_SOURCE_DIR}/src/box/vclock.c ${CMAKE_SOURCE_DIR}/src/box/errcode.c ${CMAKE_SOURCE_DIR}/src/box/error.cc) target_link_libraries(vclock.test core small) -add_executable(quota.test quota.cc test.c) +add_executable(quota.test quota.cc unit.c) target_link_libraries(quota.test pthread) add_executable(fiber.test fiber.cc) target_link_libraries(fiber.test core) +add_executable(coio.test coio.cc unit.c + ${CMAKE_SOURCE_DIR}/src/sio.cc + ${CMAKE_SOURCE_DIR}/src/evio.cc + ${CMAKE_SOURCE_DIR}/src/coio.cc + ${CMAKE_SOURCE_DIR}/src/coeio.cc + ${CMAKE_SOURCE_DIR}/src/uri.c + ${CMAKE_SOURCE_DIR}/src/fio.c + ${CMAKE_SOURCE_DIR}/src/iobuf.cc) +target_link_libraries(coio.test core eio bit) + set(MSGPUCK_DIR ${PROJECT_SOURCE_DIR}/src/lib/msgpuck/) add_executable(msgpack.test ${MSGPUCK_DIR}/test/msgpuck.c diff --git a/test/unit/coio.cc b/test/unit/coio.cc new file mode 100644 index 0000000000000000000000000000000000000000..65c28296094ae22e66c009007ffd20e2989158d5 --- /dev/null +++ b/test/unit/coio.cc @@ -0,0 +1,72 @@ +#include "memory.h" +#include "fiber.h" +#include "coio.h" +#include "fio.h" +#include "unit.h" +#include "unit.h" + +void +touch_f(va_list ap) +{ + FILE *f = va_arg(ap, FILE *); + const char *c = "c"; + while (true) { + int rc = fwrite(c, strlen(c), 1, f); + fail_unless(rc == 1); + fflush(f); + fiber_sleep(0.01); + } +} + +static void +stat_notify_test(FILE *f, const char *filename) +{ + header(); + + struct fiber *touch = fiber_new("touch", touch_f); + fiber_call(touch, f); + ev_stat stat; + note("filename: %s", filename); + coio_stat_init(&stat, filename); + coio_stat_stat_timeout(&stat, TIMEOUT_INFINITY); + fail_unless(stat.prev.st_size < stat.attr.st_size); + fiber_cancel(touch); + + footer(); +} + +static void +stat_timeout_test(const char *filename) +{ + header(); + + ev_stat stat; + coio_stat_init(&stat, filename); + coio_stat_stat_timeout(&stat, 0.01); + + footer(); +} + +void +main_f(va_list ap) +{ + const char *filename = "1.out"; + FILE *f = fopen(filename, "w+"); + stat_timeout_test(filename); + stat_notify_test(f, filename); + fclose(f); + remove(filename); + ev_break(loop(), EVBREAK_ALL); +} + +int main() +{ + memory_init(); + fiber_init(); + struct fiber *test = fiber_new("coio_stat", main_f); + fiber_wakeup(test); + ev_run(loop(), 0); + fiber_free(); + memory_free(); + return 0; +} diff --git a/test/unit/coio.result b/test/unit/coio.result new file mode 100644 index 0000000000000000000000000000000000000000..8874127d95e5fb5df992a8341cdc3e52965c1ded --- /dev/null +++ b/test/unit/coio.result @@ -0,0 +1,8 @@ +(null): fiber `touch' has been cancelled +(null): fiber `touch': exiting + *** stat_timeout_test *** + *** stat_timeout_test: done *** + *** stat_notify_test *** +# filename: 1.out + *** stat_notify_test: done *** + \ No newline at end of file diff --git a/test/unit/fiob.c b/test/unit/fiob.c index 02dce5f7721297bc19f1953342a317f802641ddc..9325b752e03429379a9822756d7f2a6a0def4685 100644 --- a/test/unit/fiob.c +++ b/test/unit/fiob.c @@ -13,7 +13,7 @@ #include <string.h> #include <errno.h> -#include "test.h" +#include "unit.h" #include <fiob.h> #include <say.h> #include <stdarg.h> diff --git a/test/unit/quota.cc b/test/unit/quota.cc index 79b49e4eec015210eb0dc50c688e21077ba3e9b2..f1cf7ae3de4b074bd95e468f8b27af03a7bd5293 100644 --- a/test/unit/quota.cc +++ b/test/unit/quota.cc @@ -3,7 +3,7 @@ #include <pthread.h> #include <sched.h> -#include "test.h" +#include "unit.h" struct quota quota; diff --git a/test/unit/rlist.c b/test/unit/rlist.c index fb0ed37eabee1532511535073dff9cccc9bd41ed..48d904e8e52e1982b357d30763cc9594638d7b07 100644 --- a/test/unit/rlist.c +++ b/test/unit/rlist.c @@ -1,7 +1,7 @@ #include "salad/rlist.h" #include <stdio.h> #include <stdarg.h> -#include "test.h" +#include "unit.h" #define PLAN 87 diff --git a/test/unit/test.h b/test/unit/test.h deleted file mode 100644 index 84478f97e72a1f10884832a032a22782aca25e1f..0000000000000000000000000000000000000000 --- a/test/unit/test.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef TARANTOOL_TEST_H_INCLUDED -#define TARANTOOL_TEST_H_INCLUDED - -#include <stdio.h> - -#if defined(__cplusplus) -extern "C" { -#endif /* defined(__cplusplus) */ - -/** -@brief example - -@code - #include "test.h" - - int main(void) { - plan(3); // count of test You planned to check - ok(1, "Test name 1"); - is(4, 2 * 2, "2 * 2 == 4"); - isnt(5, 2 * 2, "2 * 2 != 5); - return check_plan(); // print resume - } -@endcode - - -*/ - -/* private function, use ok(...) instead */ -int _ok(int condition, const char *fmt, ...); - -/* private function, use note(...) or diag(...) instead */ -void _space(FILE *stream); - -#define msg(stream, ...) ({ _space(stream); fprintf(stream, "# "); \ - fprintf(stream, __VA_ARGS__); fprintf(stream, "\n"); }) - -#define note(...) msg(stdout, __VA_ARGS__) -#define diag(...) msg(stderr, __VA_ARGS__) - -/** -@brief set and print plan -@param count -Before anything else, you need a testing plan. This basically declares -how many tests your program is going to run to protect against premature -failure. -*/ -void plan(int count); - -/** -@brief check if plan is reached and print report -*/ -int check_plan(void); - -#define ok(condition, fmt, args...) { \ - int res = _ok(condition, fmt, ##args); \ - if (!res) { \ - _space(stderr); \ - fprintf(stderr, "# Failed test '"); \ - fprintf(stderr, fmt, ##args); \ - fprintf(stderr, "'\n"); \ - _space(stderr); \ - fprintf(stderr, "# in %s at line %d\n", __FILE__, __LINE__); \ - } \ - res = res; \ -} - -#define is(a, b, fmt, args...) { \ - int res = _ok((a) == (b), fmt, ##args); \ - if (!res) { \ - _space(stderr); \ - fprintf(stderr, "# Failed test '"); \ - fprintf(stderr, fmt, ##args); \ - fprintf(stderr, "'\n"); \ - _space(stderr); \ - fprintf(stderr, "# in %s at line %d\n", __FILE__, __LINE__); \ - } \ - res = res; \ -} - -#define isnt(a, b, fmt, args...) { \ - int res = _ok((a) != (b), fmt, ##args); \ - if (!res) { \ - _space(stderr); \ - fprintf(stderr, "# Failed test '"); \ - fprintf(stderr, fmt, ##args); \ - fprintf(stderr, "'\n"); \ - _space(stderr); \ - fprintf(stderr, "# in %s at line %d\n", __FILE__, __LINE__); \ - } \ - res = res; \ -} - -#define fail(fmt, args...) \ - ok(0, fmt, ##args) - - -#if defined(__cplusplus) -} -#endif /* defined(__cplusplus) */ - -#endif /* TARANTOOL_TEST_H_INCLUDED */ - diff --git a/test/unit/test.c b/test/unit/unit.c similarity index 98% rename from test/unit/test.c rename to test/unit/unit.c index adab399153acf10d31ce771ebc4f7d7fb68e0299..86305280a5b99559f30bf8784097b24798bf3989 100644 --- a/test/unit/test.c +++ b/test/unit/unit.c @@ -1,4 +1,4 @@ -#include "test.h" +#include "unit.h" #include <stdio.h> #include <stdarg.h> diff --git a/test/unit/unit.h b/test/unit/unit.h index 4ab24ea935bb68e71501c21a0e111b734dd9757c..76d8602f7bbe34cc5aa73db9b3e9e15cafd3b8f8 100644 --- a/test/unit/unit.h +++ b/test/unit/unit.h @@ -35,7 +35,7 @@ #define header() printf("\t*** %s ***\n", __func__) #define footer() printf("\t*** %s: done ***\n ", __func__) -#define fail(expr, result) do { \ +#define fail(expr, result) do { \ fprintf(stderr, "Test failed: %s is %s at %s:%d, in function '%s'\n",\ expr, result, __FILE__, __LINE__, __func__); \ exit(-1); \ @@ -44,4 +44,95 @@ #define fail_if(expr) if (expr) fail(#expr, "true") #define fail_unless(expr) if (!(expr)) fail(#expr, "false") +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +/** +@brief example + +@code + #include "unit.h" + + int main(void) { + plan(3); // count of test You planned to check + ok(1, "Test name 1"); + is(4, 2 * 2, "2 * 2 == 4"); + isnt(5, 2 * 2, "2 * 2 != 5); + return check_plan(); // print resume + } +@endcode + + +*/ + +/* private function, use ok(...) instead */ +int _ok(int condition, const char *fmt, ...); + +/* private function, use note(...) or diag(...) instead */ +void _space(FILE *stream); + +#define msg(stream, ...) ({ _space(stream); fprintf(stream, "# "); \ + fprintf(stream, __VA_ARGS__); fprintf(stream, "\n"); }) + +#define note(...) msg(stdout, __VA_ARGS__) +#define diag(...) msg(stderr, __VA_ARGS__) + +/** +@brief set and print plan +@param count +Before anything else, you need a testing plan. This basically declares +how many tests your program is going to run to protect against premature +failure. +*/ +void plan(int count); + +/** +@brief check if plan is reached and print report +*/ +int check_plan(void); + +#define ok(condition, fmt, args...) { \ + int res = _ok(condition, fmt, ##args); \ + if (!res) { \ + _space(stderr); \ + fprintf(stderr, "# Failed test '"); \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, "'\n"); \ + _space(stderr); \ + fprintf(stderr, "# in %s at line %d\n", __FILE__, __LINE__); \ + } \ + res = res; \ +} + +#define is(a, b, fmt, args...) { \ + int res = _ok((a) == (b), fmt, ##args); \ + if (!res) { \ + _space(stderr); \ + fprintf(stderr, "# Failed test '"); \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, "'\n"); \ + _space(stderr); \ + fprintf(stderr, "# in %s at line %d\n", __FILE__, __LINE__); \ + } \ + res = res; \ +} + +#define isnt(a, b, fmt, args...) { \ + int res = _ok((a) != (b), fmt, ##args); \ + if (!res) { \ + _space(stderr); \ + fprintf(stderr, "# Failed test '"); \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, "'\n"); \ + _space(stderr); \ + fprintf(stderr, "# in %s at line %d\n", __FILE__, __LINE__); \ + } \ + res = res; \ +} + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ + #endif /* INCLUDES_TARANTOOL_TEST_UNIT_H */ diff --git a/test/unit/uri.c b/test/unit/uri.c index b978a12233d7fb5dd181878f468160aec878dd49..33e7014caf2834c4f0376914dab7d8292c51363c 100644 --- a/test/unit/uri.c +++ b/test/unit/uri.c @@ -1,4 +1,4 @@ -#include "test.h" +#include "unit.h" #include <uri.h> #include <string.h> diff --git a/test/unit/vclock.cc b/test/unit/vclock.cc index 79cd124aab9ab4011469af6f07661a5704113152..172d2637481f0912643acfed1a0314315ebfe5e1 100644 --- a/test/unit/vclock.cc +++ b/test/unit/vclock.cc @@ -27,15 +27,14 @@ * SUCH DAMAGE. */ extern "C" { -#include "test.h" +#include "unit.h" +#include "unit.h" } /* extern "C" */ #include <stdarg.h> #include "box/vclock.h" -#define header() note("*** %s ***", __func__) -#define footer() note("*** %s: done ***", __func__) #define str2(x) #x #define str(x) str2(x) #define arg(...) __VA_ARGS__ diff --git a/test/unit/vclock.result b/test/unit/vclock.result index 902b97ffc77d335ff20c75259aedf8fa01e3ce4c..334dd7ee9db6f8a6e003bfd45d05d019016ee81e 100644 --- a/test/unit/vclock.result +++ b/test/unit/vclock.result @@ -1,6 +1,6 @@ 1..5 1..40 - # *** test_compare *** + *** test_compare *** ok 1 - compare (), () => 0 ok 2 - compare (), () => 0 ok 3 - compare (), (10) => -1 @@ -41,10 +41,10 @@ ok 38 - compare (10, 10, 10, 1, 2, 3), (10, 10, 10) => 1 ok 39 - compare (0, 0, 0), (10, 0, 0, 0, 0) => -1 ok 40 - compare (10, 0, 0, 0, 0), (0, 0, 0) => 1 - # *** test_compare: done *** -ok 1 - subtests + *** test_compare: done *** + ok 1 - subtests 1..36 - # *** test_isearch *** + *** test_isearch *** ok 1 - query #1 ok 2 - query #2 ok 3 - query #3 @@ -81,10 +81,10 @@ ok 1 - subtests ok 34 - query #34 ok 35 - query #35 ok 36 - query #36 - # *** test_isearch: done *** -ok 2 - subtests + *** test_isearch: done *** + ok 2 - subtests 1..8 - # *** test_tostring *** + *** test_tostring *** ok 1 - tostring () => {} ok 2 - tostring (-1, -1, -1) => {} ok 3 - tostring (1) => {0: 1} @@ -93,10 +93,10 @@ ok 2 - subtests ok 6 - tostring (10, -1, 15, -1, 20) => {0: 10, 2: 15, 4: 20} ok 7 - tostring (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) => {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14, 15: 15} ok 8 - tostring (9223372036854775000, 9223372036854775001, 9223372036854775002, 9223372036854775003, 9223372036854775004, 9223372036854775005, 9223372036854775006, 9223372036854775007, 9223372036854775008, 9223372036854775009, 9223372036854775010, 9223372036854775011, 9223372036854775012, 9223372036854775013, 9223372036854775014, 9223372036854775015) => {0: 9223372036854775000, 1: 9223372036854775001, 2: 9223372036854775002, 3: 9223372036854775003, 4: 9223372036854775004, 5: 9223372036854775005, 6: 9223372036854775006, 7: 9223372036854775007, 8: 9223372036854775008, 9: 9223372036854775009, 10: 9223372036854775010, 11: 9223372036854775011, 12: 9223372036854775012, 13: 9223372036854775013, 14: 9223372036854775014, 15: 9223372036854775015} - # *** test_tostring: done *** -ok 3 - subtests + *** test_tostring: done *** + ok 3 - subtests 1..12 - # *** test_fromstring *** + *** test_fromstring *** ok 1 - fromstring {} => () ok 2 - fromstring { } => () ok 3 - fromstring {0: 10} => (10) @@ -109,10 +109,10 @@ ok 3 - subtests ok 10 - fromstring {0: 4294967295} => (4294967295) ok 11 - fromstring {0: 4294967296} => (4294967296) ok 12 - fromstring {0: 9223372036854775807} => (9223372036854775807) - # *** test_fromstring: done *** -ok 4 - subtests + *** test_fromstring: done *** + ok 4 - subtests 1..32 - # *** test_fromstring_invalid *** + *** test_fromstring_invalid *** ok 1 - fromstring "" => 1 ok 2 - fromstring " " => 2 ok 3 - fromstring " " => 7 @@ -145,5 +145,5 @@ ok 4 - subtests ok 30 - fromstring "{1:340282366920938463463374607431768211456}" => 43 ok 31 - fromstring "{1:10, 1:20}" => 12 ok 32 - fromstring "{1:20, 1:10}" => 12 - # *** test_fromstring_invalid: done *** -ok 5 - subtests + *** test_fromstring_invalid: done *** + ok 5 - subtests