compile/remote-compile/lbc/tool/compileServer.py (243 lines of code) (raw):
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name: compileServer
Description :
Author : liaozhaoyan
date: 2021/8/26
-------------------------------------------------
Change Activity:
2021/8/26:
-------------------------------------------------
"""
__author__ = 'liaozhaoyan'
import sys
import time
import signal
from socket import *
import json
import re
import os
import shlex
import base64
from subprocess import PIPE, Popen
from udpHook import udpPut
from multiprocessing import Process, Lock
import hashlib
from infoParse import CinfoParse
workDir = "/home/src/pylcc"
compileDir = workDir + "/lbc"
btfDir = workDir + "/hive/btf/"
dbDir = workDir + "/hive/db/"
soFile = "bpf.so"
LBC_COMPILE_PORT = 7654
# max 0xffffffff 4G
buffSize = 128 * 1024
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 ClocalTcpServer(Process):
def __init__(self, port):
self._c = CexecCmd()
self._callDict = {
"c": self._compileSo,
"btf": self._sendBtf,
"func": self._getFunc,
"struct": self._getStruct,
"type": self._getType,
}
super(ClocalTcpServer, self).__init__()
self._lock = Lock()
self.server_socket = socket(AF_INET, SOCK_STREAM)
self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)
self.server_socket.bind(("0.0.0.0", port))
self.server_socket.listen(10)
self._reSql = re.compile(r"[^a-zA-Z0-9_% ]")
def run(self):
while True:
client_socket, client_addr = self.server_socket.accept()
tr = Process(target=self.recv_data, args=(client_socket, client_addr))
tr.start()
# tr.join()
def _checkSql(self, sql):
res = self._reSql.findall(sql)
if len(res) == 0:
return True
return False
def __parseVer(self, ver):
major, minor, _ = ver.split(".", 2)
return major + "+" + minor
def _recv_lbc(self, s):
try:
d = s.recv(buffSize).decode()
except UnicodeDecodeError:
return None
if d[:3] != "LBC":
return None
size = d[3:11]
try:
size = int(size, 16) + 11
except:
return None
if size > buffSize:
return None
while len(d) < size:
d += s.recv(buffSize)
return json.loads(d[11:])
def _send_lbc(self, s, send):
send = "LBC%08x" % (len(send)) + send
s.send(send.encode())
def _setupArch(self, dRecv):
if 'arch' in dRecv:
return dRecv['arch']
return 'x86_64'
def _changeDbDir(self, dRecv):
arch = self._setupArch(dRecv)
dbPath = dbDir + f"{arch}/"
if not os.path.exists(dbPath):
return {"log": f"arch {arch} not support."}
else:
os.chdir(dbPath)
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 _dbCheckArg(self, k, v):
if ";" in v:
return {"log": f"bad {k} key."}
def __getFunc(self, dRecv):
dSend = {"log": "ok."}
ds = self._dbCheck(dRecv, "func")
if "log" in ds:
return ds
i = ds["db"]
func = dRecv["func"]
if "ret" in dRecv:
r = self._dbCheckArg("ret", dRecv["ret"])
if r:
return r
try:
dSend['res'] = i.getFuncFilterRet(dRecv["ret"], func=dRecv["func"])
return dSend
except ValueError:
return {"log": f"query value error."}
if "arg" in dRecv:
r = self._dbCheckArg("arg", dRecv["arg"])
if r: return r
try:
dSend['res'] = i.getFuncFilterArg(dRecv["arg"], func=dRecv["func"])
return dSend
except ValueError:
return {"log": f"query value error."}
if "%" in func:
dSend['res'] = i.getFuncs(func)
else:
dSend['res'] = i.getFunc(func)
return dSend
def _getFunc(self, client_socket, dRecv):
res = self._changeDbDir(dRecv)
if res['log'] != "ok.":
self._send_lbc(client_socket, json.dumps(res))
res = self.__getFunc(dRecv)
self._send_lbc(client_socket, json.dumps(res))
def __getStuct(self, dRecv):
dSend = {"log": "ok."}
ds = self._dbCheck(dRecv, "struct")
if "log" in ds:
return ds
i = ds["db"]
struct = dRecv["struct"]
dSend['res'] = i.getStruct(struct)
return dSend
def _getStruct(self, client_socket, dRecv):
res = self._changeDbDir(dRecv)
if res['log'] != "ok.":
self._send_lbc(client_socket, json.dumps(res))
self._send_lbc(client_socket, json.dumps(self.__getStuct(dRecv)))
def __getType(self, dRecv):
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 _getType(self, client_socket, dRecv):
res = self._changeDbDir(dRecv)
if res['log'] != "ok.":
self._send_lbc(client_socket, json.dumps(res))
self._send_lbc(client_socket, json.dumps(self.__getType(dRecv)))
def _transArch(self, arch):
if arch == 'x86_64':
return 'x86'
return arch
def _compileSo(self, client_socket, dRecv):
dSend = {"log": "not start", "so": None}
if "code" not in dRecv:
self._send_lbc(client_socket, json.dumps({"log": "not code."}))
return
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(dRecv['code'])
if os.path.exists(soFile):
os.remove(soFile)
ver = dRecv['ver']
arch = self._setupArch(dRecv)
self._c.cmd("rm -f bpf/vmlinux.h")
dSend['log'] = self._c.cmd('make VMLINUX_VERSION=%s ARCH=%s CARCH=%s %s' % (ver,
arch,
self._transArch(arch),
dRecv['env']))
print(dSend['log'])
try:
with open(soFile, 'rb') as f:
dSend['so'] = base64.b64encode(f.read()).decode()
except:
print("comiple fialed.")
self._send_lbc(client_socket, json.dumps(dSend))
def _sendBtf(self, client_socket, dRecv):
arch = self._setupArch(dRecv)
btfPath = btfDir + f"{arch}/"
os.chdir(btfPath)
dSend = {'ver': dRecv['ver'], 'arch': arch}
name = "vmlinux-%s" % dRecv['ver']
try:
with open(name, 'rb') as f:
dSend['btf'] = base64.b64encode(f.read()).decode()
dSend['log'] = "get btf file ok."
except IOError:
dSend['log'] = "read btf file failed."
self._send_lbc(client_socket, json.dumps(dSend))
# data format: {"cmd": "btf/c/db/func/struct", "ver"}
def _recv_data(self, client_socket):
dRecv = self._recv_lbc(client_socket)
if dRecv is None:
return
if "cmd" not in dRecv:
dRecv['log'] = 'unknown request.'
self._send_lbc(client_socket, json.dumps(dRecv))
return
if dRecv["cmd"] not in self._callDict:
dRecv['log'] = 'unknown cmd.'
self._send_lbc(client_socket, json.dumps(dRecv))
return
if "ver" not in dRecv:
dRecv['log'] = 'no kernel version info.'
self._send_lbc(client_socket, json.dumps(dRecv))
return
udpPut(dRecv)
self._callDict[dRecv['cmd']](client_socket, dRecv)
def recv_data(self, client_socket, client_addr):
self._recv_data(client_socket)
client_socket.close()
if __name__ == "__main__":
server = ClocalTcpServer(LBC_COMPILE_PORT)
server.start()
try:
signal.pause()
except KeyboardInterrupt:
print("stop")
pid = os.getpid()
os.system("kill %d" % pid)