forgot to commit openos
This commit is contained in:
42
data/openos/boot/00_base.lua
Normal file
42
data/openos/boot/00_base.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
function loadfile(filename, ...)
|
||||
if filename:sub(1,1) ~= "/" then
|
||||
filename = (os.getenv("PWD") or "/") .. "/" .. filename
|
||||
end
|
||||
local handle, open_reason = require("filesystem").open(filename)
|
||||
if not handle then
|
||||
return nil, open_reason
|
||||
end
|
||||
local buffer = {}
|
||||
while true do
|
||||
local data, reason = handle:read(1024)
|
||||
if not data then
|
||||
handle:close()
|
||||
if reason then
|
||||
return nil, reason
|
||||
end
|
||||
break
|
||||
end
|
||||
buffer[#buffer + 1] = data
|
||||
end
|
||||
return load(table.concat(buffer), "=" .. filename, ...)
|
||||
end
|
||||
|
||||
function dofile(filename)
|
||||
local program, reason = loadfile(filename)
|
||||
if not program then
|
||||
return error(reason .. ':' .. filename, 0)
|
||||
end
|
||||
return program()
|
||||
end
|
||||
|
||||
function print(...)
|
||||
local args = table.pack(...)
|
||||
local stdout = io.stdout
|
||||
local pre = ""
|
||||
for i = 1, args.n do
|
||||
stdout:write(pre, (assert(tostring(args[i]), "'tostring' must return a string to 'print'")))
|
||||
pre = "\t"
|
||||
end
|
||||
stdout:write("\n")
|
||||
stdout:flush()
|
||||
end
|
||||
84
data/openos/boot/01_process.lua
Normal file
84
data/openos/boot/01_process.lua
Normal file
@@ -0,0 +1,84 @@
|
||||
local process = require("process")
|
||||
local fs = require("filesystem")
|
||||
|
||||
--Initialize coroutine library--
|
||||
local _coroutine = coroutine -- real coroutine backend
|
||||
|
||||
_G.coroutine = setmetatable(
|
||||
{
|
||||
resume = function(co, ...)
|
||||
local proc = process.info(co)
|
||||
-- proc is nil if the process closed, natural resume will likely complain the coroutine is dead
|
||||
-- but if proc is dead and an orphan coroutine is alive, it doesn't have any proc data like stack info
|
||||
-- if the user really wants to resume it, let them
|
||||
return (proc and proc.data.coroutine_handler.resume or _coroutine.resume)(co, ...)
|
||||
end
|
||||
},
|
||||
{
|
||||
__index = function(_, key)
|
||||
local proc = process.info(_coroutine.running())
|
||||
return (proc and proc.data.coroutine_handler or _coroutine)[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
package.loaded.coroutine = _G.coroutine
|
||||
|
||||
local kernel_load = _G.load
|
||||
local intercept_load
|
||||
intercept_load = function(source, label, mode, env)
|
||||
local prev_load = env and env.load or _G.load
|
||||
local e = env and setmetatable({
|
||||
load = function(_source, _label, _mode, _env)
|
||||
return prev_load(_source, _label, _mode, _env or env)
|
||||
end}, {
|
||||
__index = env,
|
||||
__pairs = function(...) return pairs(env, ...) end,
|
||||
__newindex = function(_, key, value) env[key] = value end,
|
||||
})
|
||||
return kernel_load(source, label, mode, e or process.info().env)
|
||||
end
|
||||
_G.load = intercept_load
|
||||
|
||||
local kernel_create = _coroutine.create
|
||||
_coroutine.create = function(f,standAlone)
|
||||
local co = kernel_create(f)
|
||||
if not standAlone then
|
||||
table.insert(process.findProcess().instances, co)
|
||||
end
|
||||
return co
|
||||
end
|
||||
|
||||
_coroutine.wrap = function(f)
|
||||
local thread = coroutine.create(f)
|
||||
return function(...)
|
||||
return select(2, coroutine.resume(thread, ...))
|
||||
end
|
||||
end
|
||||
|
||||
local init_thread = _coroutine.running()
|
||||
process.list[init_thread] = {
|
||||
path = "/init.lua",
|
||||
command = "init",
|
||||
env = _ENV,
|
||||
data =
|
||||
{
|
||||
vars={},
|
||||
handles={},
|
||||
io={}, --init will populate this
|
||||
coroutine_handler = _coroutine,
|
||||
signal = error
|
||||
},
|
||||
instances = setmetatable({}, {__mode="v"})
|
||||
}
|
||||
|
||||
-- intercept fs open
|
||||
local fs_open = fs.open
|
||||
fs.open = function(...)
|
||||
local fs_open_result = table.pack(fs_open(...))
|
||||
if fs_open_result[1] then
|
||||
process.addHandle(fs_open_result[1])
|
||||
end
|
||||
return table.unpack(fs_open_result, 1, fs_open_result.n)
|
||||
end
|
||||
|
||||
41
data/openos/boot/02_os.lua
Normal file
41
data/openos/boot/02_os.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
local computer = require("computer")
|
||||
local fs = require("filesystem")
|
||||
local info = require("process").info
|
||||
local event = require("event")
|
||||
|
||||
function os.getenv(varname)
|
||||
local env = info().data.vars
|
||||
if not varname then
|
||||
return env
|
||||
elseif varname == '#' then
|
||||
return #env
|
||||
end
|
||||
return env[varname]
|
||||
end
|
||||
|
||||
function os.setenv(varname, value)
|
||||
checkArg(1, varname, "string", "number")
|
||||
if value ~= nil then
|
||||
value = tostring(value)
|
||||
end
|
||||
info().data.vars[varname] = value
|
||||
return value
|
||||
end
|
||||
|
||||
function os.sleep(timeout)
|
||||
checkArg(1, timeout, "number", "nil")
|
||||
local deadline = computer.uptime() + (timeout or 0)
|
||||
repeat
|
||||
event.pull(deadline - computer.uptime())
|
||||
until computer.uptime() >= deadline
|
||||
end
|
||||
|
||||
os.setenv("PATH", "/bin:/usr/bin:/home/bin:.")
|
||||
os.setenv("TMP", "/tmp") -- Deprecated
|
||||
os.setenv("TMPDIR", "/tmp")
|
||||
|
||||
if computer.tmpAddress() then
|
||||
fs.mount(computer.tmpAddress(), "/tmp")
|
||||
end
|
||||
|
||||
require("package").delay(os, "/lib/core/full_filesystem.lua")
|
||||
36
data/openos/boot/03_io.lua
Normal file
36
data/openos/boot/03_io.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
local buffer = require("buffer")
|
||||
local tty_stream = require("tty").stream
|
||||
|
||||
local core_stdin = buffer.new("r", tty_stream)
|
||||
local core_stdout = buffer.new("w", tty_stream)
|
||||
local core_stderr = buffer.new("w", setmetatable(
|
||||
{
|
||||
write = function(_, str)
|
||||
return tty_stream:write("\27[31m"..str.."\27[37m")
|
||||
end
|
||||
}, {__index=tty_stream}))
|
||||
|
||||
core_stdout:setvbuf("no")
|
||||
core_stderr:setvbuf("no")
|
||||
core_stdin.tty = true
|
||||
core_stdout.tty = true
|
||||
core_stderr.tty = true
|
||||
|
||||
core_stdin.close = tty_stream.close
|
||||
core_stdout.close = tty_stream.close
|
||||
core_stderr.close = tty_stream.close
|
||||
|
||||
local io_mt = getmetatable(io) or {}
|
||||
io_mt.__index = function(_, k)
|
||||
return
|
||||
k == 'stdin' and io.input() or
|
||||
k == 'stdout' and io.output() or
|
||||
k == 'stderr' and io.error() or
|
||||
nil
|
||||
end
|
||||
|
||||
setmetatable(io, io_mt)
|
||||
|
||||
io.input(core_stdin)
|
||||
io.output(core_stdout)
|
||||
io.error(core_stderr)
|
||||
203
data/openos/boot/04_component.lua
Normal file
203
data/openos/boot/04_component.lua
Normal file
@@ -0,0 +1,203 @@
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local event = require("event")
|
||||
|
||||
local adding = {}
|
||||
local primaries = {}
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- This allows writing component.modem.open(123) instead of writing
|
||||
-- component.getPrimary("modem").open(123), which may be nicer to read.
|
||||
setmetatable(component, {
|
||||
__index = function(_, key)
|
||||
return component.getPrimary(key)
|
||||
end,
|
||||
__pairs = function(self)
|
||||
local parent = false
|
||||
return function(_, key)
|
||||
if parent then
|
||||
return next(primaries, key)
|
||||
else
|
||||
local k, v = next(self, key)
|
||||
if not k then
|
||||
parent = true
|
||||
return next(primaries)
|
||||
else
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
function component.get(address, componentType)
|
||||
checkArg(1, address, "string")
|
||||
checkArg(2, componentType, "string", "nil")
|
||||
for c in component.list(componentType, true) do
|
||||
if c:sub(1, address:len()) == address then
|
||||
return c
|
||||
end
|
||||
end
|
||||
return nil, "no such component"
|
||||
end
|
||||
|
||||
function component.isAvailable(componentType)
|
||||
checkArg(1, componentType, "string")
|
||||
if not primaries[componentType] and not adding[componentType] then
|
||||
-- This is mostly to avoid out of memory errors preventing proxy
|
||||
-- creation cause confusion by trying to create the proxy again,
|
||||
-- causing the oom error to be thrown again.
|
||||
component.setPrimary(componentType, component.list(componentType, true)())
|
||||
end
|
||||
return primaries[componentType] ~= nil
|
||||
end
|
||||
|
||||
function component.isPrimary(address)
|
||||
local componentType = component.type(address)
|
||||
if componentType then
|
||||
if component.isAvailable(componentType) then
|
||||
return primaries[componentType].address == address
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function component.getPrimary(componentType)
|
||||
checkArg(1, componentType, "string")
|
||||
assert(component.isAvailable(componentType),
|
||||
"no primary '" .. componentType .. "' available")
|
||||
return primaries[componentType]
|
||||
end
|
||||
|
||||
function component.setPrimary(componentType, address)
|
||||
checkArg(1, componentType, "string")
|
||||
checkArg(2, address, "string", "nil")
|
||||
if address ~= nil then
|
||||
address = component.get(address, componentType)
|
||||
assert(address, "no such component")
|
||||
end
|
||||
|
||||
local wasAvailable = primaries[componentType]
|
||||
if wasAvailable and address == wasAvailable.address then
|
||||
return
|
||||
end
|
||||
local wasAdding = adding[componentType]
|
||||
if wasAdding and address == wasAdding.address then
|
||||
return
|
||||
end
|
||||
if wasAdding then
|
||||
event.cancel(wasAdding.timer)
|
||||
end
|
||||
primaries[componentType] = nil
|
||||
adding[componentType] = nil
|
||||
|
||||
local primary = address and component.proxy(address) or nil
|
||||
if wasAvailable then
|
||||
computer.pushSignal("component_unavailable", componentType)
|
||||
end
|
||||
if primary then
|
||||
if wasAvailable or wasAdding then
|
||||
adding[componentType] = {
|
||||
address=address,
|
||||
proxy = primary,
|
||||
timer=event.timer(0.1, function()
|
||||
adding[componentType] = nil
|
||||
primaries[componentType] = primary
|
||||
computer.pushSignal("component_available", componentType)
|
||||
end)
|
||||
}
|
||||
else
|
||||
primaries[componentType] = primary
|
||||
computer.pushSignal("component_available", componentType)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function onComponentAdded(_, address, componentType)
|
||||
local prev = primaries[componentType] or (adding[componentType] and adding[componentType].proxy)
|
||||
|
||||
if prev then
|
||||
-- special handlers -- some components are just better at being primary
|
||||
if componentType == "screen" then
|
||||
--the primary has no keyboards but we do
|
||||
if #prev.getKeyboards() == 0 then
|
||||
local first_kb = component.invoke(address, 'getKeyboards')[1]
|
||||
if first_kb then
|
||||
-- just in case our kb failed to achieve primary
|
||||
-- possible if existing primary keyboard became primary first without a screen
|
||||
-- then prev (a screen) was added without a keyboard
|
||||
-- and then we attached this screen+kb pair, and our kb fired first - failing to achieve primary
|
||||
-- also, our kb may fire right after this, which is fine
|
||||
component.setPrimary("keyboard", first_kb)
|
||||
prev = nil -- nil meaning we should take this new one over the previous
|
||||
end
|
||||
end
|
||||
elseif componentType == "keyboard" then
|
||||
-- to reduce signal noise, if this kb is also the prev, we do not need to reset primary
|
||||
if address ~= prev.address then
|
||||
--keyboards never replace primary keyboards unless the are the only keyboard on the primary screen
|
||||
local current_screen = primaries.screen or (adding.screen and adding.screen.proxy)
|
||||
--if there is not yet a screen, do not use this keyboard, it's not any better
|
||||
if current_screen then
|
||||
-- the next phase is complicated
|
||||
-- there is already a screen and there is already a keyboard
|
||||
-- this keyboard is only better if this is a keyboard of the primary screen AND the current keyboard is not
|
||||
-- i don't think we can trust kb order (1st vs 2nd), 2nd could fire first
|
||||
-- but if there are two kbs on a screen, we can give preferred treatment to the first
|
||||
-- thus, assume 2nd is not attached for the purposes of primary kb
|
||||
-- and THUS, whichever (if either) is the 1st kb of the current screen
|
||||
-- this is only possible if
|
||||
-- 1. the only kb on the system (current) has no screen
|
||||
-- 2. a screen is added without a kb
|
||||
-- 3. this kb is added later manually
|
||||
|
||||
-- prev is true when addr is not equal to the primary keyboard of the current screen -- meaning
|
||||
-- when addr is different, and thus it is not the primary keyboard, then we ignore this
|
||||
-- keyboard, and keep the previous
|
||||
-- prev is false means we should take this new keyboard
|
||||
prev = address ~= current_screen.getKeyboards()[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not prev then
|
||||
component.setPrimary(componentType, address)
|
||||
end
|
||||
end
|
||||
|
||||
local function onComponentRemoved(_, address, componentType)
|
||||
if primaries[componentType] and primaries[componentType].address == address or
|
||||
adding[componentType] and adding[componentType].address == address
|
||||
then
|
||||
local next = component.list(componentType, true)()
|
||||
component.setPrimary(componentType, next)
|
||||
|
||||
if componentType == "screen" and next then
|
||||
-- setPrimary already set the proxy (if successful)
|
||||
local proxy = (primaries.screen or (adding.screen and adding.screen.proxy))
|
||||
if proxy then
|
||||
-- if a screen is removed, and the primary keyboard is actually attached to another, non-primary, screen
|
||||
-- then the `next` screen, if it has a keyboard, should TAKE priority
|
||||
local next_kb = proxy.getKeyboards()[1] -- costly, don't call this method often
|
||||
local old_kb = primaries.keyboard or adding.keyboard
|
||||
-- if the next screen doesn't have a kb, this operation is without purpose, leave things as they are
|
||||
-- if there was no previous kb, use the new one
|
||||
if next_kb and (not old_kb or old_kb.address ~= next_kb) then
|
||||
component.setPrimary("keyboard", next_kb)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
event.listen("component_added", onComponentAdded)
|
||||
event.listen("component_removed", onComponentRemoved)
|
||||
|
||||
if _G.boot_screen then
|
||||
component.setPrimary("screen", _G.boot_screen)
|
||||
end
|
||||
_G.boot_screen = nil
|
||||
23
data/openos/boot/10_devfs.lua
Normal file
23
data/openos/boot/10_devfs.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
require("filesystem").mount(
|
||||
setmetatable({
|
||||
address = "f5501a9b-9c23-1e7a-4afe-4b65eed9b88a"
|
||||
},
|
||||
{
|
||||
__index=function(tbl,key)
|
||||
local result =
|
||||
({
|
||||
getLabel = "devfs",
|
||||
spaceTotal = 0,
|
||||
spaceUsed = 0,
|
||||
isReadOnly = false,
|
||||
})[key]
|
||||
|
||||
if result ~= nil then
|
||||
return function() return result end
|
||||
end
|
||||
local lib = require("devfs")
|
||||
lib.register(tbl)
|
||||
return lib.proxy[key]
|
||||
end
|
||||
}), "/dev")
|
||||
|
||||
9
data/openos/boot/89_rc.lua
Normal file
9
data/openos/boot/89_rc.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Run all enabled rc scripts.
|
||||
-- /boot/*rc was moved before /boot/*filesystem because
|
||||
-- 1. rc now loads in via the init signal
|
||||
-- 2. rc used to load directly
|
||||
-- Thus, for rc to load prior, it needs to register prior
|
||||
require("event").listen("init", function()
|
||||
dofile(require("shell").resolve("rc", "lua"))
|
||||
return false
|
||||
end)
|
||||
56
data/openos/boot/90_filesystem.lua
Normal file
56
data/openos/boot/90_filesystem.lua
Normal file
@@ -0,0 +1,56 @@
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local tmp = require("computer").tmpAddress()
|
||||
|
||||
local pendingAutoruns = {}
|
||||
|
||||
local function onComponentAdded(_, address, componentType)
|
||||
if componentType == "filesystem" and tmp ~= address then
|
||||
local proxy = fs.proxy(address)
|
||||
if proxy then
|
||||
local name = address:sub(1, 3)
|
||||
while fs.exists(fs.concat("/mnt", name)) and
|
||||
name:len() < address:len() -- just to be on the safe side
|
||||
do
|
||||
name = address:sub(1, name:len() + 1)
|
||||
end
|
||||
name = fs.concat("/mnt", name)
|
||||
fs.mount(proxy, name)
|
||||
if not fs.exists("/etc/filesystem.cfg") or fs.isAutorunEnabled() then
|
||||
local file = shell.resolve(fs.concat(name, "autorun"), "lua") or
|
||||
shell.resolve(fs.concat(name, ".autorun"), "lua")
|
||||
if file then
|
||||
local run = {file, _ENV, proxy}
|
||||
if pendingAutoruns then
|
||||
table.insert(pendingAutoruns, run)
|
||||
else
|
||||
xpcall(shell.execute, event.onError, table.unpack(run))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onComponentRemoved(_, address, componentType)
|
||||
if componentType == "filesystem" then
|
||||
if fs.get(shell.getWorkingDirectory()).address == address then
|
||||
shell.setWorkingDirectory("/")
|
||||
end
|
||||
fs.umount(address)
|
||||
end
|
||||
end
|
||||
|
||||
event.listen("init", function()
|
||||
for _, run in ipairs(pendingAutoruns) do
|
||||
xpcall(shell.execute, event.onError, table.unpack(run))
|
||||
end
|
||||
pendingAutoruns = nil
|
||||
return false
|
||||
end)
|
||||
|
||||
event.listen("component_added", onComponentAdded)
|
||||
event.listen("component_removed", onComponentRemoved)
|
||||
|
||||
require("package").delay(fs, "/lib/core/full_filesystem.lua")
|
||||
24
data/openos/boot/91_gpu.lua
Normal file
24
data/openos/boot/91_gpu.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
local event = require("event")
|
||||
|
||||
local function onComponentAvailable(_, componentType)
|
||||
local component = require("component")
|
||||
local tty = require("tty")
|
||||
if (componentType == "screen" and component.isAvailable("gpu")) or
|
||||
(componentType == "gpu" and component.isAvailable("screen"))
|
||||
then
|
||||
local gpu, screen = component.gpu, component.screen
|
||||
local screen_address = screen.address
|
||||
if gpu.getScreen() ~= screen_address then
|
||||
gpu.bind(screen_address)
|
||||
end
|
||||
local depth = math.floor(2^(gpu.getDepth()))
|
||||
os.setenv("TERM", "term-"..depth.."color")
|
||||
event.push("gpu_bound", gpu.address, screen_address)
|
||||
if tty.gpu() ~= gpu then
|
||||
tty.bind(gpu)
|
||||
event.push("term_available")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
event.listen("component_available", onComponentAvailable)
|
||||
12
data/openos/boot/92_keyboard.lua
Normal file
12
data/openos/boot/92_keyboard.lua
Normal file
@@ -0,0 +1,12 @@
|
||||
local event = require("event")
|
||||
local keyboard = require("keyboard")
|
||||
|
||||
local function onKeyChange(ev, _, char, code)
|
||||
-- nil might be slightly more mem friendly during runtime
|
||||
-- and `or nil` appears to only cost 30 bytes
|
||||
keyboard.pressedChars[char] = ev == "key_down" or nil
|
||||
keyboard.pressedCodes[code] = ev == "key_down" or nil
|
||||
end
|
||||
|
||||
event.listen("key_down", onKeyChange)
|
||||
event.listen("key_up", onKeyChange)
|
||||
46
data/openos/boot/93_term.lua
Normal file
46
data/openos/boot/93_term.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
local event = require("event")
|
||||
|
||||
local function components_changed(ename, address, type)
|
||||
local tty = require("tty")
|
||||
local window = tty.window
|
||||
if not window then
|
||||
return
|
||||
end
|
||||
|
||||
if ename == "component_available" or ename == "component_unavailable" then
|
||||
type = address
|
||||
end
|
||||
|
||||
if ename == "component_removed" or ename == "component_unavailable" then
|
||||
-- address can be type, when ename is *_unavailable, but *_removed works here and that's all we need
|
||||
if type == "gpu" and window.gpu.address == address then
|
||||
window.gpu = nil
|
||||
window.keyboard = nil
|
||||
elseif type == "keyboard" then
|
||||
-- we could check if this was our keyboard
|
||||
-- i.e. if address == window.keyboard
|
||||
-- but it is also simple for the terminal to
|
||||
-- recheck what kb to use
|
||||
window.keyboard = nil
|
||||
end
|
||||
if (type == "screen" or type == "gpu") and not tty.isAvailable() then
|
||||
event.push("term_unavailable")
|
||||
end
|
||||
elseif (ename == "component_added" or ename == "component_available") and type == "keyboard" then
|
||||
-- we need to clear the current terminals cached keyboard (if any) when
|
||||
-- a new keyboard becomes available. This is in case the new keyboard was
|
||||
-- attached to the terminal's window. The terminal library has the code to
|
||||
-- determine what the best keyboard to use is, but here we'll just set the
|
||||
-- cache to nil to force term library to reload it. An alternative to this
|
||||
-- method would be to make sure the terminal library doesn't cache the
|
||||
-- wrong keybaord to begin with but, users may actually expect that any
|
||||
-- primary keyboard is a valid keyboard (weird, in my opinion)
|
||||
window.keyboard = nil
|
||||
end
|
||||
end
|
||||
|
||||
event.listen("component_removed", components_changed)
|
||||
event.listen("component_added", components_changed)
|
||||
event.listen("component_available", components_changed)
|
||||
event.listen("component_unavailable", components_changed)
|
||||
|
||||
6
data/openos/boot/94_shell.lua
Normal file
6
data/openos/boot/94_shell.lua
Normal file
@@ -0,0 +1,6 @@
|
||||
-- there doesn't seem to be a reason to update $HOSTNAME after the init signal
|
||||
-- as user space /etc/profile comes after this point anyways
|
||||
if require("filesystem").exists("/etc/hostname") then
|
||||
loadfile("/bin/hostname.lua")("--update")
|
||||
end
|
||||
os.setenv("SHELL","/bin/sh.lua")
|
||||
Reference in New Issue
Block a user