// SPDX-License-Identifier: GPL-3.0-only // Copyright (c) 2022, Sylvain Huet, Ambermind // Minimacy (r) System //------------ Admin fun help()= echoLn "Minimacy admin commands"; echoLn "------------------------"; echoLn "- sys() : system overview (fs, ts, ms, inputs, timers in one command)"; echoLn "- ts() : threads status"; echoLn "- ms() : memory status"; echoLn "- fs() : list of volumes and mounts"; echoLn "- inputs() : list of active inputs (sockets, keyboard, ...)"; echoLn "- net() : list of network interfaces (only on Linux and baremetal)"; echoLn "- timers() : list of pending timers"; echoLn "- kill(id) : kill a thread based on its id"; echoLn "- dir(path) : show directory content"; echoLn "- reboot() : restart the VM"; echoLn "- gc() : force a full gc"; echoLn "- pkg() : display the current package"; echoLn "- pkgList() : display the list of current compiled packages"; echoLn "- pkgByName(name) : search a package by its name"; echoLn "- pkgScope(pkg) : display all labels visible from a given package"; echoLn "- pkgOverload(pkg): display all overloaded labels from a given package"; true;; fun ms()= gc(); let memoryRecount() -> totalMem in let memoryReserve() -> reserve in let memoryLargestBlock() -> largestBlock in let if hostMemory()<>nil then memoryFree() -> totalFree in ( echoLn "Memory:"; echoLn " Size (bytes) Package"; echoLn " ---- ------- -------"; let quicksort(pkgs(), lambda(a, b)= 0>=strCmp(pkgName(a), pkgName(b))) -> lPkgs in let listMap(lPkgs, lambda(p) = [pkgName(p), pkgMemory(p)]) -> rows in let listConcat(rows, ["TOTAL USED", totalMem]::["(reserve)", reserve]::["(free)", totalFree]::["(total ram = TOTAL USED+reserve+free)", hostMemory()]::["(largest free block)", largestBlock]::nil) -> rows in for [title, value] in rows do if value<>nil then echoLn strFormat("** *", strPadWithSpace(6, intShortFormat(1024, value)), strPadWithSpace(13, strFormat("(*)", value)), title); echoLn ""; true );; fun ts()= echoLn "Threads:"; let lambda(n1, i1, state, count, start, caller)=echoLn [strPadWithSpace(16, n1), strPadWithSpace(8, i1), strPadWithSpace(8, state), strPadWithSpace(7, count), strPadWithSpace(9, start), " ", caller] -> echoFormat in ( call echoFormat("Name", "ID", "State", "Cycles", "Start", "Origin"); call echoFormat("----", "--", "-----", "------", "-----", "------"); for th in _threads() do call echoFormat(threadName(th), strFromInt(threadId(th)), stateName(threadState(th)), intShortFormat(1000, threadCycles(th)), smallDate(threadT0(th)), threadOrigin(th)); echoLn ""; true );; fun inputs()= echoLn "Inputs:"; let lambda(type, detail, name)= echoLn strFormat("** *", strPadWithSpace(10, type), strPadWithSpace(22, detail), name) -> echoFormat in ( call echoFormat("Type", "Detail", "Thread"); call echoFormat("----", "------", "------"); for s in _StreamList do call echoFormat(streamType(s), streamDetail(s), strFormat("*: *",threadId(s._thS), s._thS._nameT)); echoLn ""; true );; fun net()= //only for baremetal or root on Unix echoLn " Name MAC IP Mask Gateway DNS"; echoLn " ---- --- -- ---- ---- ----"; for n in netList() do echoLn [ strPadWithSpace(16, n.nameI), strPadWithSpace(18, strFromMac(n.macI)), strPadWithSpace(16, strFromIp(n.ipI)), strPadWithSpace(16, strFromIp(n.submaskI)), strPadWithSpace(16, strFromIp(n.gatewayI)), strPadWithSpace(16, strFromIp(n.dnsI)), if netIsActive(n) then " Active" ]; true;; fun timers()= echoLn "Timers:"; let lambda(delay, id, name)=echoLn [strPadWithSpace(12, delay), strPadWithSpace(6, id), " ", name] -> echoFormat in let _timerList() -> lTimers in ( call echoFormat("Wait", "Id", "Thread"); call echoFormat("----", "--", "------"); for [t, th] in lTimers do call echoFormat(intWithDelimiter(",", max(0, t-timeMs())), strFromInt(threadId(th)), th._nameT); if lTimers==nil then (echoLn " (no active timer)";nil); echoLn ""; true );; fun _diskSize32bits(nbSectors, sectorSize) = if nbSectors==nil then "-" else // we complexify the next formula in order to work with 32bits integers, as nbSectors*sectorSize may exceed 2**31: let (nbSectors/(1024*1024))*sectorSize -> sizeMb in if sizeMb==0 then intShortFormat(1024, nbSectors*sectorSize) else intShortFormatWithUnits(1024, (nbSectors/(1024*1024))*sectorSize, "M"::"G"::"T"::"P"::nil);; fun volumes()= echoLn "Volumes:"; echoLn " Volume Access SectorSize NbSectors TotalSize"; echoLn " ------ ------ ---------- --------- ---------"; for v in _Volumes do echoLn [ strPadWithSpace(10, v.nameV), strPadWithSpace(10, if v.writableV then "writable" else "readonly"), strPadWithSpace(12, if v.sectorSizeV==nil then "-" else intWithDelimiter(".", v.sectorSizeV)), strPadWithSpace(14, if v.nbSectorsV==nil then "-" else intWithDelimiter(".", v.nbSectorsV)), strPadWithSpace(11, _diskSize32bits(v.nbSectorsV, v.sectorSizeV)) ]; echoLn ""; true;; fun partitions()= echoLn "Partitions:"; echoLn " Volume Access TotalSize Partition"; echoLn " ------ ------ ---------- ---------"; for v in _Volumes do for p in v.partitionsV do echoLn [ strPadWithSpace(10, v.nameV), strPadWithSpace(10, if p.writableP then "writable" else "readonly"), strPadWithSpace(12, _diskSize32bits(p.userSectorsP, p.volumeP.sectorSizeV)), " ", p.nameP ]; echoLn ""; true;; fun mounts()= echoLn "Mounts:"; let max(10, listReduce(_Mounts, 0, lambda(mx, m)= max(mx, strLength(m._mountPathM)))) -> mountWidth in ( echoLn [" Id Volume Partition Access ", strPadWithSpace(mountWidth, "Mount Path"), " Partition Path"]; echoLn [" -- ------ --------- ------ ", strCreate(mountWidth, '-'), " --------------"]; for m in _Mounts do echoLn [ strPadWithSpace(3, strFromInt(m._indexM)), strPadWithSpace(10, m._partitionM.volumeP.nameV), strPadWithSpace(18, m._partitionM.nameP), strPadWithSpace(10, if m._writableM then "writable" else "readonly"), strPadWithSpace(2+mountWidth, _strFromPath(m._mountPathM)), " ", _strFromPath(m._partitionPathM) ]; echoLn ""; true );; fun serials()= echoLn "Serial ports:"; echoLn [" Id Name"]; echoLn [" -- ----"]; for [id, name] in serialList() do echoLn [ strPadWithSpace(12, id), " ", name ]; echoLn ""; true;; fun fs()= volumes(); partitions(); mounts(); true;; fun sys()= echoLn "\nSystem status"; echoLn "=============\n"; fs(); ms(); ts(); timers(); inputs(); serials(); true;; fun kill(id)= threadStop(_threadById(id));; fun dumpCallstack(id)= let _threadById(id) -> th in if th<>nil then let lambda(n1, i1, pkg)=echoLn [strPadWithSpace(16, n1), strPadWithSpace(8, i1)] -> echoFormat in ( call echoFormat("FUNCTION", "PC", "PKG"); for [name, pc, _, pkg] in threadCallstack(th) do echoLn {strPadWithSpace(16, name), strPadWithSpace(8, strFromInt(pc)), " ", pkgName(pkg)}; true );; fun reboot()= set _HardStop=true; _reboot();; fun hardStop()= set _HardStop=true;; fun niceStop()= set _NiceStop=true;; fun mountDir(i, path) = let if i==nil then diskListInfo(path) else mountListInfo(i, path) -> l in let listFilter(l, lambda(f)= f._dirFI) -> lDirs in let listFilter(l, lambda(f)= !f._dirFI) -> lFiles in ( for p in listConcat(_filesSort(lDirs), _filesSort(lFiles)) do echoLn { strPadWithSpace(12, if p._dirFI then "DIRECTORY" else strFromInt(p._sizeFI)), " | ", if p._updateFI<>nil then fullDate(p._updateFI) else " [mounted partition]", " |", strPadWithSpace(2, strFromInt(p._mountFI)), ": ", p._nameFI }; echoLn [listLength(l), " element(s)"]; true );; fun dir(path) = mountDir(nil, path);; fun nativeFonts()= let hashsetCreate(8) -> h in ( for fontName in _nativeFontList() do hashsetAdd(h, fontName); let listFromHashset(h) -> lNames in let quicksort(lNames, lambda(a, b)= strCmp(a, b)<0) -> lNames in for fontName in lNames do echoLn fontName ); true;;