1#!/usr/bin/env tarantool 2 3local console = require('console') 4local clock = require('clock') 5local log = require('log') 6 7local CONSOLE_PORT = 3302 8 9box.cfg({}) 10console.listen(CONSOLE_PORT) 11 12-- forward declarations 13local clean 14local init 15 16-- {{{ locking 17 18local LOCK_LIFETIME = 30 -- seconds 19 20local locked_by = nil 21local locked_at = 0 -- unix time, seconds 22 23local function clean_lock() 24 locked_by = nil 25 locked_at = 0 26 clean() 27 init() 28end 29 30local function set_lock(who) 31 locked_by = who 32 locked_at = clock.monotonic() 33end 34 35local function clean_dead_lock() 36 if locked_by ~= nil and clock.monotonic() - locked_at > LOCK_LIFETIME then 37 log.info(('removed dead "%s" lock'):format(tostring(locked_by))) 38 clean_lock() 39 end 40end 41 42local function is_locked_by(who) 43 return locked_by == who 44end 45 46local function is_locked() 47 return locked_by ~= nil 48end 49 50local function acquire_lock(who) 51 assert(type(who) == 'string') 52 clean_dead_lock() 53 if is_locked_by(who) then 54 -- update lock time 55 set_lock(who) 56 log.info(('updated "%s" lock'):format(who)) 57 return true 58 end 59 if is_locked() then 60 local err = 'locked by ' .. tostring(locked_by) 61 log.info(('can not update "%s" lock: %s'):format(who, err)) 62 return false, err 63 end 64 set_lock(who) 65 log.info(('set "%s" lock'):format(who)) 66 return true 67end 68 69local function touch_lock(who) 70 assert(type(who) == 'string') 71 clean_dead_lock() 72 if is_locked_by(who) then 73 -- update lock time 74 set_lock(who) 75 log.info(('updated "%s" lock'):format(who)) 76 return true 77 end 78 if is_locked() then 79 local err = 'locked by ' .. tostring(locked_by) 80 log.info(('can not update "%s" lock: %s'):format(who, err)) 81 return false, err 82 end 83 local err = 'is not locked' 84 log.info(('can not update "%s" lock: %s'):format(who, err)) 85 return false, err 86end 87 88local function release_lock(who) 89 assert(type(who) == 'string') 90 if is_locked_by(who) then 91 clean_lock() 92 log.info(('released "%s" lock'):format(who)) 93 return true 94 end 95 clean_dead_lock() 96 if is_locked() then 97 local err = 'locked by ' .. tostring(locked_by) 98 log.info(('can not release "%s" lock: %s'):format(who, err)) 99 return false, err 100 end 101 local err = 'is not locked' 102 log.info(('can not release "%s" lock: %s'):format(who, err)) 103 return false, err 104end 105 106-- }}} 107 108-- {{{ init 109 110init = function() 111 _G.acquire_lock = acquire_lock 112 _G.touch_lock = touch_lock 113 _G.release_lock = release_lock 114end 115 116-- }}} 117 118-- {{{ clean 119 120-- Copy of cleanup_cluster() from test_run.lua. 121local function cleanup_cluster() 122 local cluster = box.space._cluster:select() 123 for _, tuple in pairs(cluster) do 124 if tuple[1] ~= box.info.id then 125 box.space._cluster:delete(tuple[1]) 126 end 127 end 128end 129 130-- Copy of clean() from pretest_clean.lua from test-run. 131clean = function() 132 local _SPACE_NAME = 3 133 134 box.space._space:pairs():map(function(tuple) 135 local name = tuple[_SPACE_NAME] 136 return name 137 end):filter(function(name) 138 -- skip internal spaces 139 local first_char = string.sub(name, 1, 1) 140 return first_char ~= '_' 141 end):each(function(name) 142 box.space[name]:drop() 143 end) 144 145 local _USER_TYPE = 4 146 local _USER_NAME = 3 147 148 local allowed_users = { 149 guest = true, 150 admin = true, 151 } 152 box.space._user:pairs():filter(function(tuple) 153 local tuple_type = tuple[_USER_TYPE] 154 return tuple_type == 'user' 155 end):map(function(tuple) 156 local name = tuple[_USER_NAME] 157 return name 158 end):filter(function(name) 159 return not allowed_users[name] 160 end):each(function(name) 161 box.schema.user.drop(name) 162 end) 163 164 local allowed_roles = { 165 public = true, 166 replication = true, 167 super = true, 168 } 169 box.space._user:pairs():filter(function(tuple) 170 local tuple_type = tuple[_USER_TYPE] 171 return tuple_type == 'role' 172 end):map(function(tuple) 173 local name = tuple[_USER_NAME] 174 return name 175 end):filter(function(name) 176 return not allowed_roles[name] 177 end):each(function(name) 178 box.schema.role.drop(name) 179 end) 180 181 local _FUNC_NAME = 3 182 local allowed_funcs = { 183 ['box.schema.user.info'] = true, 184 } 185 box.space._func:pairs():map(function(tuple) 186 local name = tuple[_FUNC_NAME] 187 return name 188 end):filter(function(name) 189 return not allowed_funcs[name] 190 end):each(function(name) 191 box.schema.func.drop(name) 192 end) 193 194 cleanup_cluster() 195 196 local cleanup_list = function(list, allowed) 197 for k, _ in pairs(list) do 198 if not allowed[k] then 199 list[k] = nil 200 end 201 end 202 end 203 204 local allowed_globals = { 205 -- modules 206 bit = true, 207 coroutine = true, 208 debug = true, 209 io = true, 210 jit = true, 211 math = true, 212 os = true, 213 package = true, 214 string = true, 215 table = true, 216 utf8 = true, 217 -- variables 218 _G = true, 219 _VERSION = true, 220 arg = true, 221 -- functions 222 assert = true, 223 collectgarbage = true, 224 dofile = true, 225 error = true, 226 gcinfo = true, 227 getfenv = true, 228 getmetatable = true, 229 ipairs = true, 230 load = true, 231 loadfile = true, 232 loadstring = true, 233 module = true, 234 next = true, 235 pairs = true, 236 pcall = true, 237 print = true, 238 rawequal = true, 239 rawget = true, 240 rawset = true, 241 require = true, 242 select = true, 243 setfenv = true, 244 setmetatable = true, 245 tonumber = true, 246 tonumber64 = true, 247 tostring = true, 248 type = true, 249 unpack = true, 250 xpcall = true, 251 -- tarantool 252 _TARANTOOL = true, 253 box = true, 254 dostring = true, 255 help = true, 256 newproxy = true, 257 role_check_grant_revoke_of_sys_priv = true, 258 tutorial = true, 259 update_format = true, 260 } 261 cleanup_list(_G, allowed_globals) 262 263 local allowed_packages = { 264 ['_G'] = true, 265 bit = true, 266 box = true, 267 ['box.backup'] = true, 268 ['box.internal'] = true, 269 ['box.internal.sequence'] = true, 270 ['box.internal.session'] = true, 271 ['box.internal.space'] = true, 272 buffer = true, 273 clock = true, 274 console = true, 275 coroutine = true, 276 crypto = true, 277 csv = true, 278 debug = true, 279 digest = true, 280 errno = true, 281 ffi = true, 282 fiber = true, 283 fio = true, 284 fun = true, 285 help = true, 286 ['help.en_US'] = true, 287 ['http.client'] = true, 288 iconv = true, 289 ['internal.argparse'] = true, 290 ['internal.trigger'] = true, 291 io = true, 292 jit = true, 293 ['jit.bc'] = true, 294 ['jit.bcsave'] = true, 295 ['jit.dis_x64'] = true, 296 ['jit.dis_x86'] = true, 297 ['jit.dump'] = true, 298 ['jit.opt'] = true, 299 ['jit.p'] = true, 300 ['jit.profile'] = true, 301 ['jit.util'] = true, 302 ['jit.v'] = true, 303 ['jit.vmdef'] = true, 304 ['jit.zone'] = true, 305 json = true, 306 log = true, 307 math = true, 308 msgpack = true, 309 msgpackffi = true, 310 ['net.box'] = true, 311 ['net.box.lib'] = true, 312 os = true, 313 package = true, 314 pickle = true, 315 pwd = true, 316 socket = true, 317 strict = true, 318 string = true, 319 table = true, 320 ['table.clear'] = true, 321 ['table.new'] = true, 322 tap = true, 323 tarantool = true, 324 title = true, 325 uri = true, 326 utf8 = true, 327 uuid = true, 328 xlog = true, 329 yaml = true, 330 } 331 cleanup_list(package.loaded, allowed_packages) 332 333 local user_count = box.space._user:count() 334 assert(user_count == 4 or user_count == 5, 335 'box.space._user:count() should be 4 (1.10) or 5 (2.0)') 336 assert(box.space._func:count() == 1, 337 'box.space._func:count() should be only one') 338 assert(box.space._cluster:count() == 1, 339 'box.space._cluster:count() should be only one') 340 341 box.cfg({listen = box.NULL}) 342end 343 344-- }}} 345 346clean() 347init() 348