commit 84abd9bcf1f816721639c13969fed01e4dc75bd5 Author: pauljako Date: Thu Feb 27 15:00:15 2025 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06474a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bndd diff --git a/client.go b/client.go new file mode 100644 index 0000000..6b3284e --- /dev/null +++ b/client.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/json" + "errors" + "log" + "net" +) + +func client(conn net.Conn, instance string) error { + log.Println("Client connected") + buffer := make([]byte, 1024) + n, err := conn.Read(buffer) + message := string(buffer[:n]) + if err != nil { + log.Fatal(err) + } + log.Println("Message Received:", message) + var objmap map[string]string + err = json.Unmarshal([]byte(message), &objmap) + if err != nil { + log.Println(err) + return err + } + val, has := objmap["command"] + if has { + switch val { + case "run": + _, err = runMap(conn, instance, objmap) + if err != nil { + log.Println(err) + return err + } + default: + err = errors.New("Unknown Command: " + val) + log.Println(err) + return err + } + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..203d942 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module boundaries/bndd + +go 1.23.4 diff --git a/main.go b/main.go new file mode 100644 index 0000000..97e4df3 --- /dev/null +++ b/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "errors" + "io/fs" + "log" + "net" + "os" + "os/signal" + "path/filepath" + "syscall" +) + +func main() { + running := true + instance_path, err := filepath.Abs("/home/pauljako/boundaries") + if err != nil { + log.Fatal(err) + } + instance, err := filepath.EvalSymlinks(instance_path) + if err != nil { + log.Fatal(err) + } + socket_path, err := filepath.Abs(filepath.Join(instance, "/boundaries.sock")) + if err != nil { + log.Fatal(err) + } + ln, err := net.Listen("unix", socket_path) + if err != nil { + log.Fatal(err) + } + log.Println("Listening on", instance+"/boundaries.sock") + signalChannel := make(chan os.Signal, 1) + signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) + go func() { + sig := <-signalChannel + switch sig { + case os.Interrupt: + running = false + log.Println("Received interrupt") + ln.Close() + os.Exit(0) + case syscall.SIGTERM: + running = false + log.Println("Received SIGTERM") + ln.Close() + os.Exit(15) + } + }() + + accept_clients(&ln, &instance, &running) +} + +func exists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if errors.Is(err, fs.ErrNotExist) { + return false, nil + } + return false, err +} + +func accept_clients(ln *net.Listener, instance *string, running *bool) { + for *running { + conn, err := (*ln).Accept() + if err != nil { + if !(*running) { + continue + } + log.Println(err) + } + go func() { + err := client(conn, *instance) + if err != nil { + conn.Write([]byte(err.Error())) + } + conn.Close() + }() + } +} diff --git a/run.go b/run.go new file mode 100644 index 0000000..250b787 --- /dev/null +++ b/run.go @@ -0,0 +1,95 @@ +package main + +import ( + "encoding/json" + "errors" + "io" + "net" + "os" + "os/exec" + "path/filepath" +) + +func runMap(conn net.Conn, instance string, objmap map[string]string) (int, error) { + program, has := objmap["package"] + if !has { + return 0, errors.New("a package is required") + } + arguments, has := objmap["arguments"] + if !has { + arguments = "" + } + target, has := objmap["target"] + if !has { + target = "run" + } + workdir, has := objmap["workdir"] + if !has { + return 0, errors.New("a workdir is required") + } + return run(conn, instance, program, arguments, target, workdir) +} + +func run(conn net.Conn, instance string, program string, arguments string, target string, workdir string) (int, error) { + package_path, err := filepath.Abs(filepath.Join(instance, "apps", program)) + if err != nil { + return 0, err + } + + info_path, err := filepath.Abs(filepath.Join(package_path, "boundaries.json")) + if err != nil { + return 0, err + } + if exists, err := exists(info_path); !exists || err != nil { + return 0, errors.New("package " + program + " not found") + } + + working_dir, err := filepath.Abs(workdir) + if err != nil { + return 0, err + } + + infofile_content, err := os.ReadFile(info_path) + if err != nil { + return 0, err + } + + var info map[string]interface{} + err = json.Unmarshal(infofile_content, &info) + if err != nil { + return 0, err + } + + _, has := info["name"] + if !has { + return 0, errors.New("invalid boundaries.json file. name field not found") + } + + command_interface, has := info["command"] + if !has { + return 0, errors.New("invalid boundaries.json file. command field not found") + } + + commands := command_interface.(map[string]interface{}) + + command, has := commands[target] + if !has { + return 0, errors.New("target " + target + " not found") + } + + command, err = filepath.Abs(filepath.Join(package_path, command.(string))) + if err != nil { + return 0, err + } + cmd := exec.Command("sh", "-c", command.(string)+" "+arguments) + cmd.Dir = working_dir + cmd.Stdout = io.MultiWriter(conn, os.Stdout) + cmd.Stderr = io.MultiWriter(conn, os.Stderr) + cmd.Stdin = conn + err = cmd.Run() + if err != nil { + return 0, err + } + + return 0, nil +}