compile/remote-compile/lbc/tool/surfServer.py (240 lines of code) (raw):
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name: surfServer
Description :
Author : liaozhaoyan
date: 2022/5/9
-------------------------------------------------
Change Activity:
2022/5/9:
-------------------------------------------------
"""
__author__ = 'liaozhaoyan'
import os
import re
import base64
from subprocess import PIPE, Popen
import shlex
from udpHook import udpPut
from infoParse import CinfoParse
from getFuncs import CgenfuncsDb
rootDir = "/home"
dbDir = os.path.join(rootDir, "hive/db")
btfDir = os.path.join(rootDir, "hive/btf")
compileDir = os.path.join(rootDir, "lbc")
koBuildDir = os.path.join(rootDir, "lbc/ko")
elfSoDir = os.path.join(rootDir, "lbc/elf_sym")
soFile = "bpf.so"
objFile = ".output/lbc.bpf.o"
dfFile = "pre.db"
SEG_UNIT = 4096
def segDecode(stream):
line = b""
l = len(stream)
for i in range(0, l, 4 * SEG_UNIT):
s = stream[i:i + 4 * SEG_UNIT]
line += base64.b64decode(s)
if l % (4 * SEG_UNIT):
i = int(l / (4 * SEG_UNIT) * (4 * SEG_UNIT))
line += base64.b64decode(stream[i:])
return line
def segEncode(stream):
line = b""
l = len(stream)
for i in range(0, l, 3 * SEG_UNIT):
s = stream[i:i+3 * SEG_UNIT]
line += base64.b64encode(s)
if l % (3 * SEG_UNIT):
i = int(l / (3 * SEG_UNIT) * (3 * SEG_UNIT))
line += base64.b64encode(stream[i:])
return line
class CexecCmd(object):
def __init__(self):
pass
def cmd(self, cmds):
p = Popen(shlex.split(cmds), stdout=PIPE, stderr=PIPE)
return p.stdout.read().decode() + p.stderr.read().decode()
def system(self, cmds):
cmds = cmds.replace('\0', '').strip()
return os.system(cmds)
class CsurfServer(object):
def __init__(self, lock, udpHost):
self._callDict = {
"btf": self._sendBtf,
"func": self._getFunc,
"struct": self._getStruct,
"type": self._getType,
"c": self._compileSo,
"obj": self._compileObj,
"ko": self._koBuild,
"elf_so": self._sendElfSo,
}
self._reSql = re.compile(r"[^a-zA-Z0-9_% ]")
self._lock = lock
self._c = CexecCmd()
self._udpHost = udpHost
def _checkSql(self, sql):
res = self._reSql.findall(sql)
if len(res) == 0:
return True
return False
def _setupArch(self, dRecv):
if 'arch' in dRecv:
return dRecv['arch']
return 'x86_64'
def _changeDbDir(self, dRecv):
arch = self._setupArch(dRecv)
dbPath = os.path.join(dbDir, arch)
if not os.path.exists(dbPath):
return {"log": f"arch {arch} not support."}
else:
os.chdir(dbPath)
return {"log": "ok."}
def _changeKoDir(self):
if not os.path.exists(koBuildDir):
return {"log": f"arch not support."}
else:
os.chdir(koBuildDir)
return {"log": "ok."}
def _dbCheck(self, dRecv, k):
path = f"info-{dRecv['ver']}.db"
try:
i = CinfoParse(path)
except IOError:
return {"log": "version not support."}
if k not in dRecv or dRecv[k] == "" or not self._checkSql(dRecv[k]):
return {"log": f"bad {k} key."}
return {"db": i}
def _getType(self, dRecv):
res = self._changeDbDir(dRecv)
if res['log'] != "ok.":
return res
dSend = {"log": "ok."}
ds = self._dbCheck(dRecv, "type")
if "log" in ds:
return ds
i = ds["db"]
t = dRecv["type"]
dSend['res'] = i.getType(t)
return dSend
def _getStruct(self, dRecv):
res = self._changeDbDir(dRecv)
if res['log'] != "ok.":
return res
dSend = {"log": "ok."}
ds = self._dbCheck(dRecv, "struct")
if "log" in ds:
return ds
i = ds["db"]
t = dRecv["struct"]
dSend['res'] = i.getStruct(t)
return dSend
def _getFunc(self, dRecv):
res = self._changeDbDir(dRecv)
if res['log'] != "ok.":
return res
dSend = {"log": "ok."}
ds = self._dbCheck(dRecv, "func")
if "log" in ds:
return ds
i = ds["db"]
t = dRecv["func"]
dSend['res'] = i.getFunc(t)
return dSend
def _sendElfSo(self, dRecv):
arch = self._setupArch(dRecv)
soPath = os.path.join(elfSoDir, arch)
os.chdir(soPath)
dSend = {'ver': dRecv['ver'], 'arch': arch}
try:
with open("syms.so", "rb") as f:
dSend['so'] = segEncode(f.read()).decode()
dSend['log'] = "ok."
except IOError:
dSend['log'] = "read elf so file failed."
return dSend
def _sendBtf(self, dRecv):
arch = self._setupArch(dRecv)
btfPath = os.path.join(btfDir, arch)
os.chdir(btfPath)
dSend = {'ver': dRecv['ver'], 'arch': arch}
name = "vmlinux-%s" % dRecv['ver']
try:
with open(name, 'rb') as f:
dSend['btf'] = segEncode(f.read()).decode()
dSend['log'] = "ok."
except IOError:
dSend['log'] = "read btf file failed."
return dSend
def _transArch(self, arch):
if arch == 'x86_64':
return 'x86'
return arch
def _setupWorkPre(self, path):
self._c.cmd(f"rm -rf {path}")
os.mkdir(path)
os.chdir(path)
def _setupWorkEnd(self, path):
os.chdir("../")
self._c.cmd(f"rm -rf {path}")
def _koBuild(self, dRecv):
try:
kos = dRecv.pop("kos")
except KeyError:
return {"log": "kos not in request."}
res = self._changeKoDir()
if res['log'] != "ok.":
return res
dSend = {"log": "ok."}
path = "tmp"
with self._lock:
self._setupWorkPre(path)
for k, v in kos.items():
with open(k, 'wb') as f:
f.write(segDecode(v))
fns = CgenfuncsDb(dfFile, dRecv['arch'])
fns.parse_kos('./')
del fns # confirm all data write to db
with open(dfFile, 'rb') as f:
dSend['db'] = segEncode(f.read()).decode()
self._setupWorkEnd(path)
return dSend
def _make(self, dRecv, filePath, k):
dSend = {"log": "not start", k: None}
try:
cStr = dRecv.pop("code")
except KeyError:
return {"log": "no code."}
if 'env' not in dRecv:
dRecv['env'] = ""
with self._lock:
print("compile for %s" % dRecv['ver'])
os.chdir(compileDir)
with open("bpf/lbc.bpf.c", 'w') as f:
f.write(cStr)
if os.path.exists(filePath):
os.remove(filePath)
ver = dRecv['ver']
arch = self._setupArch(dRecv)
self._c.cmd("rm -f bpf/vmlinux.h")
dSend['clog'] = self._c.cmd('make VMLINUX_VERSION=%s ARCH=%s CARCH=%s CLFLAG="%s"' % (ver,
arch,
self._transArch(arch),
dRecv['env']))
print(dSend['clog'])
# self._c.cmd("strip -x %s" % filePath)
try:
with open(filePath, 'rb') as f:
dSend[k] = base64.b64encode(f.read()).decode()
dSend['log'] = "ok."
except (OSError, IOError) as e:
dSend['log'] = f"setup {k} report: {e}."
print(dSend)
return dSend
def _compileSo(self, dRecv):
return self._make(dRecv, soFile, 'so')
def _compileObj(self, dRecv):
return self._make(dRecv, objFile, 'obj')
def proc(self, dRecv):
if "cmd" not in dRecv:
dRecv['log'] = 'unknown request.'
return dRecv
if dRecv["cmd"] not in self._callDict:
dRecv['log'] = 'unknown cmd.'
return dRecv
if "ver" not in dRecv:
dRecv['log'] = 'no kernel version info.'
return dRecv
res = self._callDict[dRecv['cmd']](dRecv)
udpPut(self._udpHost, dRecv)
return res
if __name__ == "__main__":
pass