package main
import (
"bufio"
"bytes"
"context"
"fmt"
"google.golang.org/grpc"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"os/exec"
pb "proto"
"regexp"
"strings"
"sync"
"github.com/pkg/errors"
"time"
"crypto/md5"
)
type Status int
const (
Building Status = 0
Builded Status = 1
NotBuilded Status = -1
)
const (
port = ":50051"
pathToLkmSrc = "lkm"
gcc4 = "/usr/bin/gcc-4.8"
gcc7 = "/usr/bin/gcc-7"
)
var workingDir string
func check(e error) {
if e != nil {
log.Fatal(e)
panic(e)
}
}
type KernelInfo struct {
pathToKernel string
status Status
}
var UbuntuUrls = []string{}
var UbuntuUrlsMutex = sync.RWMutex{}
var KernelMap = map[string]KernelInfo{}
var KernelMapMutex = sync.Mutex{}
type server struct{}
func downloadAndUnpackKernel(dirname string, url string) error {
err := exec.Command("wget", "-O", dirname + "/" + "deb.deb", url).Run()
if (err != nil) {
return err
}
arCmd := exec.Command("ar", "-x", dirname + "/" + "deb.deb")
arCmd.Dir = dirname
err = arCmd.Run()
if (err != nil) {
return err
}
untarCmd := exec.Command("tar", "-xf", "data.tar.xz")
untarCmd.Dir = dirname
err = untarCmd.Run()
if (err != nil) {
return err
}
//UbuntuUrls = append(UbuntuUrls,workingDir + "/" + kernelVersion)
return nil
}
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(65 + rand.Intn(25)) //A=65 and Z = 65+25
}
return string(bytes)
}
func buildUbuntuKernel(kernelVersion string) (data []byte, err error) {
log.Print("Ubuntu")
kernelVersion = strings.Replace(kernelVersion, "\n", "", -1)
//isGeneric, _ := regexp.MatchString(".+-generic", kernelVersion)
var kernelKey, allKernelKey string
var allKernelPattern string
kernelKernelPattern := ".+" + kernelVersion + ".+amd64.deb"
//FIXME ััะตั low_latency
version := strings.Replace(kernelVersion, "-generic", "", -1)
allKernelPattern = ".+" + version + ".+_all.deb"
log.Printf("Kernel version %v", allKernelPattern)
UbuntuUrlsMutex.RLock()
defer UbuntuUrlsMutex.RUnlock()
for _, key := range UbuntuUrls {
kernelMatch, _ := regexp.MatchString(kernelKernelPattern, key)
if kernelMatch {
log.Printf("Kernel match %v", key)
kernelKey = key
}
allKernelMatch, _ := regexp.MatchString(allKernelPattern, key)
if allKernelMatch {
log.Printf("All match %v", key)
allKernelKey = key
}
}
log.Printf("allKernelKey %v", allKernelKey)
dirname := randomString(22);
dirname = workingDir + "/" + dirname
err = os.MkdirAll(dirname, 0700)
if (err != nil) {
return nil, err
}
defer os.RemoveAll(dirname)
if len(allKernelKey) > 0 {
err := downloadAndUnpackKernel(dirname, allKernelKey)
if (err != nil) {
return nil, err
}
} else {
return nil, errors.New(fmt.Sprintf("Can't download All kernel, can't find url (version = %s)", version))
}
if len(kernelKey) > 0 {
err := downloadAndUnpackKernel(dirname, kernelKey)
if (err != nil) {
return nil, err
}
} else {
return nil, errors.New(fmt.Sprintf("Can't download kernel, can't find url (version = %s)", version))
}
sed := exec.Command("bash", "-c", "find " + dirname + "/ -type f -exec sed -i 's/-fstack-protector-strong/ /g' {} +")
sed.Dir = dirname
err = sed.Run()
if (err != nil) {
return nil, err
}
kernelDir := dirname + "/usr/src/linux-headers-"+kernelVersion
buildDir := dirname + "/build";
log.Print(kernelDir)
cmd := exec.Command("bash", "-c", "make CC=" + gcc4 + " BUILD="+buildDir + " KDIR="+kernelDir+" -C " + pathToLkmSrc)
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err = cmd.Run()
if (err != nil) {
return nil, errors.Wrap(err, out.String() + stderr.String())
}
return ioutil.ReadFile(buildDir + "/" + "hook.ko")
}
func (s *server) Send(ctx context.Context, in *pb.Request) (*pb.Reply, error) {
/*astra1_11Url := "https://download.astralinux.ru/astra/frozen/orel-1.11/repository/pool/main/l/linux/"
astra1_11Hrefs, _ := exec.Command("wget", "-k", astra1_11Url).Output()
astra2_12Url := "https://download.astralinux.ru/astra/current/orel/repository/pool/main/l/linux/"
astra2_12Hrefs, _ := exec.Command("wget", "-k", astra2_12Url).Output()
*/
sysInfoArray := strings.Split(in.SystemInfo, "\n")
systemInfoMap := make(map[string]string)
for _, line := range sysInfoArray {
tmp := strings.Split(line, `:`)
if len(tmp) == 2 {
key, value := tmp[0], tmp[1]
systemInfoMap[key] = value
}
}
isAstra := strings.Contains(systemInfoMap["Description"], "Astra")
kernelVersion := in.KernelVersion
//kernelVersion := "4.4.0-21-generic"
var data []byte
var err error
if isAstra {
switch systemInfoMap["Release"] {
case "1.11":
case "2.12":
}
} else { // Ubuntu
data, err = buildUbuntuKernel(kernelVersion)
}
log.Printf("Module Size : %v MD5 %x", len(data), md5.Sum(data))
if (err != nil) {
return nil, err
}
return &pb.Reply{Message: data}, nil
}
func downloadUbuntuRep() {
ubuntuUrl := "http://us.archive.ubuntu.com/ubuntu/pool/main/l/linux/"
log.Print("wget")
_ = exec.Command("wget", "-O", "ubuntu.html", "-k", ubuntuUrl).Run()
validURL := regexp.MustCompile(`href="(.+linux-headers[^>"]+)"`)
file, err := os.Open("ubuntu.html")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
match := validURL.FindAllStringSubmatch(scanner.Text(), -1)
if match != nil {
for _, lst := range match {
UbuntuUrls = append(UbuntuUrls, lst[1])
log.Printf("%s\n", lst[1])
}
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Print("wget end")
UbuntuUrlsMutex.Unlock()
}
func main() {
rand.Seed(time.Now().UTC().UnixNano())
var err error
workingDir, err = os.Getwd()
workingDir += "/tmp/"
if (err != nil) {
panic(err)
}
UbuntuUrlsMutex.Lock()
go downloadUbuntuRep()
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}