diff --git a/Packet.lua b/Packet.lua new file mode 100644 index 0000000..d143814 --- /dev/null +++ b/Packet.lua @@ -0,0 +1,36 @@ +return { + package = { + name = "packets", + id = "packets@1.0.0", + version = "1.0.0", + author = "robogg133", + description = "fast, opensource, easy to use package manager.", + type = "remote", + serial = 0, + + build_dependencies = {["go"] = ">=1.25.1"}, + + git_url = "https://github.com/roboogg133/packets.git", + git_branch = "main" + + }, + + + prepare = function(container) + git.clone("https://github.com/roboogg133/packets.git", container.dir("/data")) + os.remove(container.dir("/data/.git")) + + end, + + build = function() + os.execute("go build ./data/cmd/packets") + end, + + install = function(container) + os.copy(container.dir("./packets"), BIN_DIR) + end, + + remove = function () + os.remove(path_join(BIN_DIR, "packets")) + end +} \ No newline at end of file diff --git a/cmd/httpsocket/main.go b/cmd/httpsocket/main.go index b4db14e..7def4b5 100644 --- a/cmd/httpsocket/main.go +++ b/cmd/httpsocket/main.go @@ -18,7 +18,7 @@ type ConfigTOML struct { } func main() { - + log.Println("Program started") cfg, err := configs.GetConfigTOML() if err != nil { log.Fatal(err) @@ -31,6 +31,6 @@ func main() { fs := http.FileServer(http.Dir(cfg.Config.Cache_d)) http.Handle("/", fs) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.Config.HttpPort), nil)) + log.Printf("Listening and serving on port %d\n", cfg.Config.HttpPort) } diff --git a/cmd/packets/main.go b/cmd/packets/main.go index af72a91..ed8463c 100644 --- a/cmd/packets/main.go +++ b/cmd/packets/main.go @@ -2,8 +2,8 @@ package main import ( "database/sql" - _ "embed" "fmt" + "io" "log" "os" "path/filepath" @@ -14,11 +14,13 @@ import ( "packets/configs" "packets/internal/consts" errors_packets "packets/internal/errors" + "packets/internal/packet" "packets/internal/utils" packets "packets/pkg" "github.com/pelletier/go-toml/v2" "github.com/spf13/cobra" + lua "github.com/yuin/gopher-lua" _ "modernc.org/sqlite" ) @@ -258,7 +260,6 @@ var installCmd = &cobra.Command{ var wg sync.WaitGroup wg.Add(1) go AsyncFullyUpgrade(inputName, cfg.Config.StorePackages, filepath.Join(cfg.Config.Data_d, id), &wg, db) - wg.Done() continue } @@ -362,18 +363,27 @@ var removeCmd = &cobra.Command{ log.Fatal(err) } - f, err := os.Open(filepath.Join(packageDir, "manifest.toml")) + f, err := os.Open(filepath.Join(packageDir, "Packet.lua")) if err != nil { log.Fatal(err) } - manifest, err := utils.ManifestFileRead(f) + fBLob, err := io.ReadAll(f) + if err != nil { + log.Fatal(err) + } + + manifest, err := packet.ReadPacket(fBLob) if err != nil { log.Fatal(err) } fmt.Println(":: Removing", pkgName) - packets.ExecuteRemoveScript(filepath.Join(packageDir, manifest.Hooks.Remove)) + os.Chdir(packageDir) + + if err := manifest.ExecuteRemove(lua.NewState()); err != nil { + log.Panic(err) + } if err := os.RemoveAll(packageDir); err != nil { log.Fatal(err) @@ -649,7 +659,7 @@ func UpgradeToThis(id string, installPath string, installedDB *sql.DB, storePkgF serial = ?, package_d = ?, filename = ?, os = ?, arch = ?, in_cache = ? `, p.QueryName, - p.Manifest.Info.Id, + p.Manifest.Id, p.Version, p.Description, p.Serial, diff --git a/cmd/udpsocket/main.go b/cmd/udpsocket/main.go index 328bd68..5e1bd0e 100644 --- a/cmd/udpsocket/main.go +++ b/cmd/udpsocket/main.go @@ -23,6 +23,7 @@ func CheckDownloaded(filename string) bool { } func main() { + log.Println("Program started") pid := os.Getpid() if err := os.WriteFile(filepath.Join(consts.DefaultLinux_d, "udp.pid"), []byte(fmt.Sprint(pid)), 0664); err != nil { fmt.Println("error saving subprocess pid", err) @@ -37,6 +38,7 @@ func main() { if err != nil { log.Fatal(err) } + log.Println("Started connection listener") defer conn.Close() buf := make([]byte, 1500) @@ -46,12 +48,15 @@ func main() { log.Println("error creating udp socket", err) } msg := string(buf[:n]) + log.Printf("Received message : %s\n", msg) if !strings.HasPrefix(msg, "Q:") { + log.Println("error: invalid message, this message don't follow the protocol") continue } filename := strings.TrimPrefix(msg, "Q:") if CheckDownloaded(filename) { reply := fmt.Sprintf("H:%s:%d", filename, cfg.Config.HttpPort) + log.Printf("Package founded in cache dir, answering with: '%s'\n", reply) conn.WriteToUDP([]byte(reply), remote) } } diff --git a/configs/structs.go b/configs/structs.go index 21813b1..5650059 100644 --- a/configs/structs.go +++ b/configs/structs.go @@ -1,19 +1,32 @@ package configs +/* type Manifest struct { - Info struct { + Package struct { Name string `toml:"name"` Id string `toml:"id"` Version string `toml:"version"` Description string `toml:"description"` Dependencies map[string]string `toml:"dependencies"` Author string `toml:"author"` - } `toml:"Info"` + Architeture string `toml:"architeture"` + Os string `toml:"os"` + PacakgeType string `toml:"type"` + + GitUrl string `toml:"giturl,omitempty"` + Branch string `toml:"gitbranch,omitempty"` + } `toml:"Package"` + Build struct { + BuildDependencies map[string]string `toml:"dependencies"` + } Hooks struct { + Fetch string `toml:"fetch,omitempty"` Install string `toml:"install"` Remove string `toml:"remove"` + Build string `toml:"build"` } `toml:"Hooks"` } +*/ type ConfigTOML struct { Config struct { diff --git a/doc/indexdbSchema.txt b/doc/indexdbSchema.txt index 25b712e..3432159 100644 --- a/doc/indexdbSchema.txt +++ b/doc/indexdbSchema.txt @@ -13,6 +13,7 @@ CREATE TABLE packages ( arch TEXT NOT NULL, os TEXT NOT NULL, size INTEGER NOT NULL DEFAULT 0, + type TEXT NOT NULL DEFAULT 'static', UNIQUE(query_name, version), UNIQUE(query_name, serial) @@ -33,7 +34,6 @@ CREATE TABLE IF NOT EXISTS packages ( query_name TEXT NOT NULL UNIQUE PRIMARY KEY, id TEXT NOT NULL UNIQUE, version TEXT NOT NULL, - dependencies TEXT NOT NULL DEFAULT '', description TEXT NOT NULL, package_d TEXT NOT NULL, filename TEXT NOT NULL, @@ -41,7 +41,16 @@ CREATE TABLE IF NOT EXISTS packages ( arch TEXT NOT NULL, in_cache INTEGER NOT NULL DEFAULT 1, serial INTEGER NOT NULL, + type TEXT NOT NULL, UNIQUE(query_name, version), UNIQUE(query_name, serial) +); + + + +CREATE TABLE IF NOT EXISTS build_dependencies ( + id TEXT PRIMARY KEY, + dir TEXT NOT NULL DEFAULT "/dev/null" + uses INTEGER NOT NULL DEFAULT 0 ); \ No newline at end of file diff --git a/go.mod b/go.mod index d4c9d89..4c688b2 100644 --- a/go.mod +++ b/go.mod @@ -4,50 +4,63 @@ go 1.25.1 require ( github.com/gin-gonic/gin v1.11.0 + github.com/go-git/go-git/v6 v6.0.0-20251021092831-91c33c9361ce github.com/klauspost/compress v1.18.0 github.com/pelletier/go-toml/v2 v2.2.4 + github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.1 github.com/yuin/gopher-lua v1.1.1 - golang.org/x/net v0.44.0 + golang.org/x/net v0.46.0 modernc.org/sqlite v1.38.2 ) require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/cloudwego/base64x v0.1.6 // indirect + github.com/cyphar/filepath-securejoin v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-git/gcfg/v2 v2.0.2 // indirect + github.com/go-git/go-billy/v6 v6.0.0-20251016063423-4289a4e54aa4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-yaml v1.18.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.4.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.54.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/sergi/go-diff v1.4.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/arch v0.20.0 // indirect - golang.org/x/crypto v0.42.0 // indirect + golang.org/x/crypto v0.43.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.27.0 // indirect + golang.org/x/mod v0.28.0 // indirect golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.29.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/tools v0.37.0 // indirect google.golang.org/protobuf v1.36.9 // indirect modernc.org/libc v1.66.3 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index 941aff8..046ff98 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,47 @@ +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw= +github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo= +github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= +github.com/go-git/go-billy/v6 v6.0.0-20251016063423-4289a4e54aa4 h1:xQLkKmWcw9n5CUkl//bKQB7SPWQbaoKSVnHXH9V8sg8= +github.com/go-git/go-billy/v6 v6.0.0-20251016063423-4289a4e54aa4/go.mod h1:TpCYxdQ0tWZkrnAkd7yqK+z1C8RKcyjcaYAJNAcnUnM= +github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w= +github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU= +github.com/go-git/go-git/v6 v6.0.0-20251021092831-91c33c9361ce h1:DFvDcCiFZk2yDXVreC9+B+SAZYHH2RssMmtR2zBwANE= +github.com/go-git/go-git/v6 v6.0.0-20251021092831-91c33c9361ce/go.mod h1:xtOWa43AoQlsqYogmpf0MnjBJHKPL2/3teh4fmZ/k+Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -28,6 +54,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -39,10 +67,15 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= +github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -55,6 +88,8 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= +github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= @@ -64,6 +99,10 @@ github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -73,6 +112,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -88,26 +128,31 @@ go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/build/complete.go b/internal/build/complete.go new file mode 100644 index 0000000..e25a401 --- /dev/null +++ b/internal/build/complete.go @@ -0,0 +1,80 @@ +package build + +import ( + "packets/internal/packet" + utils_lua "packets/internal/utils/lua" + + lua "github.com/yuin/gopher-lua" +) + +func (container Container) ExecutePrepare(packetLua packet.PacketLua, L *lua.LState) error { + + if packetLua.Prepare == nil { + return nil + } + + gitTable := L.NewTable() + + gitTable.RawSetString("clone", L.NewFunction(utils_lua.LGitClone)) + gitTable.RawSetString("checkout", L.NewFunction(utils_lua.LGitCheckout)) + gitTable.RawSetString("pull", L.NewFunction(utils_lua.LGitPUll)) + + containerTable := L.NewTable() + + containerTable.RawSetString("dir", L.NewFunction(container.lDir)) + + L.SetGlobal("git", gitTable) + L.Push(packetLua.Prepare) + L.Push(containerTable) + + return L.PCall(1, 0, nil) + +} + +func (container Container) ExecuteBuild(packetLua packet.PacketLua, L *lua.LState) error { + + if packetLua.Build == nil { + return nil + } + + osObject := L.GetGlobal("os").(*lua.LTable) + ioObject := L.GetGlobal("io").(*lua.LTable) + + OnlyContainerOS := L.NewTable() + + OnlyContainerOS.RawSetString("copy", L.NewFunction(container.lCopy)) + OnlyContainerOS.RawSetString("mkdir", L.NewFunction(container.lMkdir)) + OnlyContainerOS.RawSetString("rename", L.NewFunction(container.lRename)) + OnlyContainerOS.RawSetString("remove", L.NewFunction(container.lRemove)) + OnlyContainerOS.RawSetString("execute", L.NewFunction(container.lexecute)) + OnlyContainerOS.RawSetString("open", L.NewFunction(container.lOpen)) + + OnlyContainerIO := L.NewTable() + + OnlyContainerIO.RawSetString("popen", L.NewFunction(container.lpopen)) + + L.SetGlobal("io", OnlyContainerIO) + L.SetGlobal("os", OnlyContainerOS) + + L.Push(packetLua.Build) + err := L.PCall(0, 0, nil) + if err != nil { + return err + } + + L.SetGlobal("os", osObject) + L.SetGlobal("io", ioObject) + return nil +} + +func (container Container) ExecuteInstall(packetLua packet.PacketLua, L *lua.LState) error { + + containerTable := L.NewTable() + + containerTable.RawSetString("dir", L.NewFunction(container.lDir)) + + L.Push(packetLua.Install) + L.Push(containerTable) + + return L.PCall(1, 0, nil) +} diff --git a/internal/build/install.go b/internal/build/install.go new file mode 100644 index 0000000..cb58bc3 --- /dev/null +++ b/internal/build/install.go @@ -0,0 +1,167 @@ +package build + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "log" + "os" + "packets/internal/packet" + "packets/internal/utils" + utils_lua "packets/internal/utils/lua" + "path/filepath" + "strings" + "sync" + + "github.com/klauspost/compress/zstd" +) + +func (container Container) installPackage(file []byte, destDir string) error { + manifest, err := packet.ReadPacketFromFile(bytes.NewReader(file)) + if err != nil { + return err + } + + zstdReader, err := zstd.NewReader(bytes.NewReader(file)) + if err != nil { + return err + } + defer zstdReader.Close() + + tarReader := tar.NewReader(zstdReader) + + uid, err := utils.GetPacketsUID() + if err != nil { + return err + } + + for { + hdr, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + rel := filepath.Clean(hdr.Name) + + if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + continue + } + + if err := os.MkdirAll(filepath.Join(container.Root, destDir), 0775); err != nil { + return err + } + + if err := os.Chown(filepath.Join(container.Root, destDir), uid, 0); err != nil { + return err + } + + absPath := filepath.Join(filepath.Join(container.Root, destDir), rel) + + switch hdr.Typeflag { + + case tar.TypeDir: + err = os.MkdirAll(absPath, os.FileMode(hdr.Mode)) + if err != nil { + return err + } + if err := os.Chown(absPath, uid, 0); err != nil { + return err + } + + case tar.TypeReg: + + err = os.MkdirAll(filepath.Dir(absPath), 0775) + if err != nil { + return err + } + + out, err := os.Create(absPath) + if err != nil { + return err + } + + _, err = io.Copy(out, tarReader) + out.Close() + if err != nil { + return err + } + + err = os.Chmod(absPath, os.FileMode(0775)) + if err != nil { + return err + } + + if filepath.Base(hdr.Name) == "Packet.lua" { + err = os.Chmod(absPath, os.FileMode(0755)) + if err != nil { + return err + } + } else { + if err := os.Chown(absPath, uid, 0); err != nil { + return err + } + } + } + } + + L, err := utils_lua.GetSandBox() + if err != nil { + return err + } + + bootstrapcontainer, err := NewContainer(manifest) + if err != nil { + return err + } + + os.Chdir(destDir) + + if err := bootstrapcontainer.ExecutePrepare(manifest, &L); err != nil { + return fmt.Errorf("error executing prepare: %s", err) + } + + if err := bootstrapcontainer.ExecuteBuild(manifest, &L); err != nil { + return fmt.Errorf("error executing build: %s", err) + } + + if err := utils.ChangeToNoPermission(); err != nil { + return fmt.Errorf("error changing to packet user: %s", err) + } + if err := bootstrapcontainer.ExecuteInstall(manifest, &L); err != nil { + return fmt.Errorf("error executing build: %s", err) + } + + if err := utils.ElevatePermission(); err != nil { + return fmt.Errorf("error changing to root: %s", err) + } + + return nil +} + +func (container Container) asyncFullInstallDependencie(dep string, storePackages bool, installPath string, wg *sync.WaitGroup) { + defer wg.Done() + + fmt.Printf(" downloading %s \n", dep) + p, err := utils.GetPackage(dep) + if err != nil { + log.Fatal(err) + } + + fmt.Printf(" installing %s \n", dep) + + if err := container.installPackage(p.PackageF, installPath); err != nil { + log.Fatal(err) + } + + if storePackages { + _, err := p.Write() + if err != nil { + log.Fatal(err) + return + } + } +} diff --git a/internal/build/lua.go b/internal/build/lua.go new file mode 100644 index 0000000..9f0f648 --- /dev/null +++ b/internal/build/lua.go @@ -0,0 +1,224 @@ +package build + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + utils_lua "packets/internal/utils/lua" + + "github.com/spf13/afero" + lua "github.com/yuin/gopher-lua" +) + +func (container Container) lRemove(L *lua.LState) int { + filename := L.CheckString(1) + + err := container.FS.RemoveAll(filename) + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + L.Push(lua.LTrue) + L.Push(lua.LNil) + return 2 +} + +func (container Container) lRename(L *lua.LState) int { + oldname := L.CheckString(1) + newname := L.CheckString(2) + + if err := container.FS.Rename(oldname, newname); err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + L.Push(lua.LTrue) + return 1 +} + +func (container Container) lCopy(L *lua.LState) int { + oldname := L.CheckString(1) + newname := L.CheckString(2) + + if err := container.copyContainer(oldname, newname); err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + L.Push(lua.LTrue) + L.Push(lua.LNil) + return 2 +} + +func modeFlags(mode string) int { + switch mode { + case "r", "rb": + return os.O_RDONLY + case "w", "wb": + return os.O_CREATE | os.O_WRONLY | os.O_TRUNC + case "a", "ab": + return os.O_CREATE | os.O_WRONLY | os.O_APPEND + case "r+", "r+b", "rb+", "br+": + return os.O_RDWR + case "w+", "w+b", "wb+", "bw+": + return os.O_CREATE | os.O_RDWR | os.O_TRUNC + case "a+", "a+b", "ab+", "ba+": + return os.O_CREATE | os.O_RDWR | os.O_APPEND + default: + return os.O_RDONLY + } +} + +func (container Container) lOpen(L *lua.LState) int { + path := L.CheckString(1) + mode := L.OptString(2, "r") + + file, err := container.FS.OpenFile(path, modeFlags(mode), 0o644) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(err.Error())) + return 2 + } + + ud := L.NewUserData() + ud.Value = file + L.SetMetatable(ud, L.GetTypeMetatable("file")) + L.Push(ud) + L.Push(lua.LNil) + return 2 +} + +func (container Container) lMkdir(L *lua.LState) int { + path := L.CheckString(1) + perm := L.CheckInt(2) + + if err := container.FS.MkdirAll(path, os.FileMode(perm)); err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + L.Push(lua.LTrue) + L.Push(lua.LNil) + return 2 +} + +func (container Container) lexecute(L *lua.LState) int { + cmdString := L.CheckString(1) + + cmdSlice := strings.Fields(cmdString) + + files, err := afero.ReadDir(container.FS, BinDir) + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString("exit")) + L.Push(lua.LNumber(127)) + return 3 + } + + for _, file := range files { + if !file.IsDir() && file.Name() == cmdSlice[0] { + err := exec.Command(cmdSlice[0], cmdSlice[1:]...).Run() + if err != nil { + if errr := err.(*exec.ExitError); errr != nil { + L.Push(lua.LFalse) + + if err.(*exec.ExitError).Exited() { + L.Push(lua.LString("exit")) + } else { + L.Push(lua.LString("signal")) + } + + L.Push(lua.LNumber(err.(*exec.ExitError).ExitCode())) + return 3 + } + } + L.Push(lua.LTrue) + L.Push(lua.LString("exit")) + L.Push(lua.LNumber(0)) + } + } + + L.Push(lua.LFalse) + L.Push(lua.LString("exit")) + L.Push(lua.LNumber(127)) + return 3 +} + +func (container Container) lpopen(L *lua.LState) int { + cmdString := L.CheckString(1) + mode := L.CheckString(2) + + cmdSlice := strings.Fields(cmdString) + + files, err := afero.ReadDir(container.FS, BinDir) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString("can't find executable")) + return 2 + } + + for _, file := range files { + if !file.IsDir() && file.Name() == cmdSlice[0] { + cmd := exec.Command(cmdSlice[0], cmdSlice[1:]...) + output, _ := cmd.CombinedOutput() + + switch mode { + case "r": + ud := L.NewUserData() + ud.Value = string(output) + L.SetMetatable(ud, L.GetTypeMetatable("file")) + L.Push(ud) + L.Push(lua.LNil) + case "w": + ud := L.NewUserData() + ud.Value = string(output) + os.Stdout.Write(output) + L.SetMetatable(ud, L.GetTypeMetatable("file")) + L.Push(ud) + L.Push(lua.LNil) + default: + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("%s: Invalid argument", cmdString))) + } + + } + } + + L.Push(lua.LNil) + L.Push(lua.LString("can't find any executable")) + return 2 +} + +func (container Container) lDir(L *lua.LState) int { + dir := L.CheckString(1) + + L.Push(lua.LString(filepath.Join(container.Root, filepath.Clean(dir)))) + return 1 +} + +func (container Container) GetLuaState() { + + L := lua.NewState() + osObject := L.GetGlobal("os").(*lua.LTable) + ioObject := L.GetGlobal("io").(*lua.LTable) + + L.SetGlobal("path_join", L.NewFunction(utils_lua.Ljoin)) + + osObject.RawSetString("remove", L.NewFunction(container.lRemove)) + osObject.RawSetString("rename", L.NewFunction(container.lRename)) + osObject.RawSetString("copy", L.NewFunction(container.lCopy)) + + osObject.RawSetString("mkdir", L.NewFunction(container.lMkdir)) + + ioObject.RawSetString("popen", L.NewFunction(container.lpopen)) + ioObject.RawSetString("open", L.NewFunction(container.lOpen)) + + container.LuaState = *L +} diff --git a/internal/build/main.go b/internal/build/main.go new file mode 100644 index 0000000..ae3d65f --- /dev/null +++ b/internal/build/main.go @@ -0,0 +1,183 @@ +package build + +import ( + "database/sql" + "encoding/base64" + "encoding/json" + "io" + "os" + "packets/internal/consts" + "packets/internal/packet" + "path/filepath" + + _ "modernc.org/sqlite" + + "github.com/spf13/afero" + lua "github.com/yuin/gopher-lua" +) + +type Container struct { + BuildID BuildID + Root string + FS afero.Fs + LuaState lua.LState + Manifest packet.PacketLua + uses int + DeleteAfter bool +} + +func NewContainer(manifest packet.PacketLua) (Container, error) { + + var container Container + var err error + container.BuildID, err = getBuildId(manifest.BuildDependencies) + if err != nil { + return Container{}, err + } + baseFs := afero.NewOsFs() + + db, err := sql.Open("sqlite", consts.InstalledDB) + if err != nil { + return Container{}, err + } + if err := db.QueryRow("SELECT uses, dir FROM build_dependencies WHERE id = ? ", container.BuildID).Scan(&container.uses, container.Root); err != nil { + db.Close() + return Container{}, err + } + db.Close() + + if container.Root != "/dev/null" { + if _, err := os.Stat(container.Root); err != nil { + if os.IsNotExist(err) { + if err := container.createNew(); err != nil { + return Container{}, err + } + } + } + } else { + container.DeleteAfter = true + if err := container.createNew(); err != nil { + return Container{}, err + } + } + + container.GetLuaState() + fileSystem := afero.NewBasePathFs(baseFs, container.Root) + + container.Manifest = manifest + container.FS = fileSystem + + if err := container.FS.MkdirAll(BinDir, 0777); err != nil { + return Container{}, err + } + + if err := container.FS.MkdirAll("/etc/packets", 0777); err != nil { + return Container{}, err + } + + if err := afero.Symlinker.SymlinkIfPossible(container.FS.(afero.Symlinker), BinDir, SymLinkBinDir); err != nil { + return Container{}, err + } + + return container, nil +} + +func (container Container) CopyHostToContainer(src string, dest string) error { + stats, err := os.Stat(src) + if err != nil { + return err + } + + if stats.IsDir() { + files, err := os.ReadDir(src) + if err != nil { + return err + } + + if err := container.FS.MkdirAll(dest, 0755); err != nil { + return err + } + + for _, file := range files { + srcPath := filepath.Join(src, file.Name()) + destPath := filepath.Join(dest, file.Name()) + + if file.IsDir() { + if err := container.CopyHostToContainer(srcPath, destPath); err != nil { + return err + } + continue + } + + if err := container.copySingleFile(srcPath, destPath); err != nil { + return err + } + } + } else { + if err := container.copySingleFile(src, dest); err != nil { + return err + } + } + + return nil +} + +func (container Container) copySingleFile(source string, destination string) error { + src, err := os.Open(source) + if err != nil { + return err + } + defer src.Close() + + stats, err := src.Stat() + if err != nil { + return err + } + if err := container.FS.MkdirAll(filepath.Dir(destination), 0755); err != nil { + return err + } + dst, err := container.FS.Create(destination) + if err != nil { + return err + } + defer dst.Close() + + if _, err := io.Copy(dst, src); err != nil { + return err + } + + if err := container.FS.Chmod(destination, stats.Mode()); err != nil { + return err + } + + return nil +} + +func getBuildId(buildDependencies map[string]string) (BuildID, error) { + blobs, err := json.Marshal(buildDependencies) + if err != nil { + return "", err + } + return BuildID(base64.StdEncoding.EncodeToString(blobs)), nil +} + +func (container Container) saveBuild() error { + db, err := sql.Open("sqlite", consts.InstalledDB) + if err != nil { + return err + } + defer db.Close() + + buildID := container.BuildID + var exists bool + if err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM build_dependencies WHERE id = ?)", buildID).Scan(exists); err != nil { + return err + } + if exists { + _, err := db.Exec("UPDATE FROM build_dependencies WHERE id = ? SET uses = uses + 1", buildID) + return err + } + + _, err = db.Exec("INSERT INTO build_dependencies (id) VALUES (?)", buildID) + return err +} diff --git a/internal/build/manager.go b/internal/build/manager.go new file mode 100644 index 0000000..3880efe --- /dev/null +++ b/internal/build/manager.go @@ -0,0 +1,45 @@ +package build + +import ( + "os" + "packets/configs" + "packets/internal/consts" + "packets/internal/utils" + "path/filepath" + "sync" + + _ "modernc.org/sqlite" +) + +func (container Container) createNew() error { + if err := os.MkdirAll(filepath.Join(consts.BuildImagesDir, string(container.BuildID)), 0775); err != nil { + return err + } + packetsuid, err := utils.GetPacketsUID() + if err != nil { + return err + } + if err := os.Chown(filepath.Join(consts.BuildImagesDir, string(container.BuildID)), packetsuid, 0); err != nil { + return err + } + dependencies, err := utils.ResolvDependencies(container.Manifest.BuildDependencies) + if err != nil { + return err + } + + cfg, err := configs.GetConfigTOML() + if err != nil { + return err + } + var wg sync.WaitGroup + for _, depn := range dependencies { + wg.Add(1) + go container.asyncFullInstallDependencie(depn, cfg.Config.StorePackages, depn, &wg) + } + wg.Wait() + + container.Root = filepath.Join(consts.BuildImagesDir, string(container.BuildID)) + + container.saveBuild() + return nil +} diff --git a/internal/build/specs.go b/internal/build/specs.go new file mode 100644 index 0000000..be62122 --- /dev/null +++ b/internal/build/specs.go @@ -0,0 +1,8 @@ +package build + +const ( + BinDir = "/usr/bin" + SymLinkBinDir = "/bin" +) + +type BuildID string // Json dependencies encoded to base64 stdEncoding diff --git a/internal/build/utils.go b/internal/build/utils.go new file mode 100644 index 0000000..1f645ba --- /dev/null +++ b/internal/build/utils.go @@ -0,0 +1,79 @@ +package build + +import ( + "io" + "path/filepath" + + "github.com/spf13/afero" +) + +func (container Container) copyContainer(src string, dest string) error { + stats, err := container.FS.Stat(src) + if err != nil { + return err + } + + if stats.IsDir() { + files, err := afero.ReadDir(container.FS, src) + if err != nil { + return err + } + + if err := container.FS.MkdirAll(dest, 0755); err != nil { + return err + } + + for _, file := range files { + srcPath := filepath.Join(src, file.Name()) + destPath := filepath.Join(dest, file.Name()) + + if file.IsDir() { + if err := container.CopyHostToContainer(srcPath, destPath); err != nil { + return err + } + continue + } + + if err := container.copySingleFileLtoL(srcPath, destPath); err != nil { + return err + } + } + } else { + if err := container.copySingleFileLtoL(src, dest); err != nil { + return err + } + } + + return nil +} + +func (container Container) copySingleFileLtoL(source string, destination string) error { + src, err := container.FS.Open(source) + if err != nil { + return err + } + defer src.Close() + + stats, err := src.Stat() + if err != nil { + return err + } + if err := container.FS.MkdirAll(filepath.Dir(destination), 0755); err != nil { + return err + } + dst, err := container.FS.Create(destination) + if err != nil { + return err + } + defer dst.Close() + + if _, err := io.Copy(dst, src); err != nil { + return err + } + + if err := container.FS.Chmod(destination, stats.Mode()); err != nil { + return err + } + + return nil +} diff --git a/internal/consts/consts.go b/internal/consts/consts.go index 182078a..0fdd938 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -11,6 +11,7 @@ const ( LANDeadline = 2 * time.Second IndexDB = "/etc/packets/index.db" InstalledDB = "/etc/packets/installed.db" + BuildImagesDir = "/etc/packets/temp" DefaultSyncUrl = "https://servidordomal.fun/index.db" ) @@ -18,7 +19,6 @@ const InstalledDatabaseSchema = `CREATE TABLE IF NOT EXISTS packages ( query_name TEXT NOT NULL UNIQUE PRIMARY KEY, id TEXT NOT NULL UNIQUE, version TEXT NOT NULL, - dependencies TEXT NOT NULL DEFAULT '', description TEXT NOT NULL, package_d TEXT NOT NULL, filename TEXT NOT NULL, @@ -26,6 +26,7 @@ const InstalledDatabaseSchema = `CREATE TABLE IF NOT EXISTS packages ( arch TEXT NOT NULL, in_cache INTEGER NOT NULL DEFAULT 1, serial INTEGER NOT NULL, + type TEXT NOT NULL, UNIQUE(query_name, version), UNIQUE(query_name, serial) @@ -40,4 +41,10 @@ CREATE TABLE package_dependencies( ); CREATE INDEX index_dependency_name ON package_dependencies(dependency_name); + +CREATE TABLE IF NOT EXISTS build_dependencies ( + id TEXT PRIMARY KEY, + dir TEXT NOT NULL DEFAULT "/dev/null" + uses INTEGER NOT NULL DEFAULT 0 +); ` diff --git a/internal/errors/errors.go b/internal/errors/errors.go index e97c965..657ea4e 100644 --- a/internal/errors/errors.go +++ b/internal/errors/errors.go @@ -4,7 +4,7 @@ import "errors" var ( ErrResponseNot200OK = errors.New("the request is not 200, download failed") - ErrCantFindManifestTOML = errors.New("can't find manifest.toml when trying to read the packagefile") + ErrCantFindPacketDotLua = errors.New("can't find manifest.toml when trying to read the packagefile") ErrInvalidSignature = errors.New("the signature is invalid") ErrNotInstalled = errors.New("the package isn't installed") ErrAlredyUpToDate = errors.New("alredy up to date") diff --git a/internal/packet/auxiliar_functions.go b/internal/packet/auxiliar_functions.go new file mode 100644 index 0000000..6039ec9 --- /dev/null +++ b/internal/packet/auxiliar_functions.go @@ -0,0 +1,65 @@ +package packet + +import lua "github.com/yuin/gopher-lua" + +func getStringFromTable(table *lua.LTable, key string) string { + value := table.RawGetString(key) + if value.Type() == lua.LTString { + return value.String() + } + return "" +} + +func getIntFromTable(table *lua.LTable, key string) int { + value := table.RawGetString(key) + if value.Type() == lua.LTNumber { + if num, ok := value.(lua.LNumber); ok { + return int(num) + } + } + return -133 +} + +func getStringArrayFromTable(L *lua.LState, table *lua.LTable, key string) []string { + value := table.RawGetString(key) + if value.Type() != lua.LTTable { + return []string{} + } + + arrayTable := value.(*lua.LTable) + var result []string + + arrayTable.ForEach(func(_, value lua.LValue) { + if value.Type() == lua.LTString { + result = append(result, value.String()) + } + }) + + return result +} + +func getFunctionFromTable(table *lua.LTable, key string) *lua.LFunction { + value := table.RawGetString(key) + if value.Type() == lua.LTFunction { + return value.(*lua.LFunction) + } + return nil +} + +func getDependenciesFromTable(L *lua.LState, table *lua.LTable, key string) map[string]string { + value := table.RawGetString(key) + if value.Type() != lua.LTTable { + return map[string]string{} + } + + depsTable := value.(*lua.LTable) + dependencies := make(map[string]string) + + depsTable.ForEach(func(key, value lua.LValue) { + if key.Type() == lua.LTString && value.Type() == lua.LTString { + dependencies[key.String()] = value.String() + } + }) + + return dependencies +} diff --git a/internal/packet/main.go b/internal/packet/main.go new file mode 100644 index 0000000..015c23a --- /dev/null +++ b/internal/packet/main.go @@ -0,0 +1,182 @@ +package packet + +import ( + "archive/tar" + "fmt" + "io" + "packets/configs" + errors_packets "packets/internal/errors" + "path/filepath" + "runtime" + + "github.com/go-git/go-git/v6" + "github.com/go-git/go-git/v6/plumbing" + "github.com/go-git/go-git/v6/storage/memory" + "github.com/klauspost/compress/zstd" + lua "github.com/yuin/gopher-lua" +) + +type PacketLua struct { + Name string + Id string + Version string + Description string + Dependencies map[string]string + Author string + Architetures []string + Os []string + Serial int + Type string + + PkgType string + GitUrl string + GitBranch string + + BuildDependencies map[string]string + + Prepare *lua.LFunction + Build *lua.LFunction + Install *lua.LFunction + Remove *lua.LFunction +} + +// ReadPacket read a Packet.lua and alredy set global vars +func ReadPacket(f []byte) (PacketLua, error) { + cfg, err := configs.GetConfigTOML() + if err != nil { + return PacketLua{}, err + } + L := lua.NewState() + defer L.Close() + + osObject := L.GetGlobal("os").(*lua.LTable) + ioObject := L.GetGlobal("io").(*lua.LTable) + + L.SetGlobal("os", lua.LNil) + L.SetGlobal("io", lua.LNil) + + L.SetGlobal("BIN_DIR", lua.LString(cfg.Config.Bin_d)) + L.SetGlobal("ARCH", lua.LString(runtime.GOARCH)) + L.SetGlobal("OS", lua.LString(runtime.GOOS)) + + if err := L.DoString(string(f)); err != nil { + return PacketLua{}, err + } + + L.SetGlobal("os", osObject) + L.SetGlobal("io", ioObject) + + tableLua := L.Get(-1) + + if tableLua.Type() != lua.LTTable { + return PacketLua{}, fmt.Errorf("invalid Packet.lua format: the file do not return a table") + } + + table := tableLua.(*lua.LTable) + + pkgTableLua := table.RawGetString("package") + if pkgTableLua.Type() != lua.LTTable { + return PacketLua{}, fmt.Errorf("invalid Packet.lua format: can't find package table") + } + pkgTable := pkgTableLua.(*lua.LTable) + + packetLua := &PacketLua{ + Name: getStringFromTable(pkgTable, "name"), + Id: getStringFromTable(pkgTable, "id"), + Version: getStringFromTable(pkgTable, "version"), + Author: getStringFromTable(pkgTable, "author"), + Description: getStringFromTable(pkgTable, "description"), + PkgType: getStringFromTable(pkgTable, "type"), + Serial: getIntFromTable(pkgTable, "serial"), + + Dependencies: getDependenciesFromTable(L, pkgTable, "dependencies"), + BuildDependencies: getDependenciesFromTable(L, pkgTable, "build_dependencies"), + + GitUrl: getStringFromTable(pkgTable, "git_url"), + GitBranch: getStringFromTable(pkgTable, "git_branch"), + + Os: getStringArrayFromTable(L, pkgTable, "os"), + Architetures: getStringArrayFromTable(L, pkgTable, "arch"), + + Prepare: getFunctionFromTable(table, "prepare"), + Build: getFunctionFromTable(table, "build"), + Install: getFunctionFromTable(table, "install"), + Remove: getFunctionFromTable(table, "remove"), + } + + if packetLua.Install == nil || packetLua.Remove == nil { + return PacketLua{}, fmt.Errorf("install or remove function is not valid") + } + + return *packetLua, nil +} + +func (packetLua PacketLua) ExecuteRemove(L *lua.LState) error { + L.Push(packetLua.Remove) + return L.PCall(0, 0, nil) +} + +func ReadPacketFromFile(file io.Reader) (PacketLua, error) { + + zstdReader, err := zstd.NewReader(file) + if err != nil { + return PacketLua{}, err + } + defer zstdReader.Close() + + tarReader := tar.NewReader(zstdReader) + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + return PacketLua{}, err + } + + if filepath.Base(header.Name) == "Packet.lua" { + + packageLuaBlob, err := io.ReadAll(tarReader) + if err != nil { + return PacketLua{}, err + } + + return ReadPacket(packageLuaBlob) + } + + } + return PacketLua{}, errors_packets.ErrCantFindPacketDotLua +} + +func GetPackageDotLuaFromRemote(url string, branch string) (PacketLua, error) { + + repo, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ + Depth: 1, + URL: url, + SingleBranch: true, + ReferenceName: plumbing.ReferenceName("refs/heads/" + branch), + }) + if err != nil { + return PacketLua{}, err + } + ref, err := repo.Head() + if err != nil { + return PacketLua{}, err + } + commit, err := repo.CommitObject(ref.Hash()) + if err != nil { + return PacketLua{}, err + } + + f, err := commit.File("Packet.lua") + if err != nil { + return PacketLua{}, err + } + + content, err := f.Contents() + if err != nil { + return PacketLua{}, err + } + + return ReadPacket([]byte(content)) +} diff --git a/internal/packet/utils.go b/internal/packet/utils.go new file mode 100644 index 0000000..9c40969 --- /dev/null +++ b/internal/packet/utils.go @@ -0,0 +1 @@ +package packet diff --git a/internal/utils/lua/git.go b/internal/utils/lua/git.go new file mode 100644 index 0000000..3c81474 --- /dev/null +++ b/internal/utils/lua/git.go @@ -0,0 +1,130 @@ +package utils_lua + +import ( + "fmt" + "os" + + "github.com/go-git/go-git/v6" + "github.com/go-git/go-git/v6/plumbing" + lua "github.com/yuin/gopher-lua" +) + +func LGitClone(L *lua.LState) int { + uri := L.CheckString(1) + output := L.CheckString(2) + + depth := 1 + if L.GetTop() > 2 { + depth = L.CheckInt(3) + } + + _, err := git.PlainClone(output, &git.CloneOptions{ + URL: uri, + Progress: os.Stdout, + Depth: depth, + }) + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + L.Push(lua.LTrue) + L.Push(lua.LNil) + return 2 +} + +func LGitCheckout(L *lua.LState) int { + dir := L.CheckString(1) + branchorid := L.CheckString(2) + + repo, err := git.PlainOpen(dir) + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + worktree, err := repo.Worktree() + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + if err := tryCheckout(*worktree, branchorid); err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + L.Push(lua.LTrue) + L.Push(lua.LNil) + return 2 +} +func LGitPUll(L *lua.LState) int { + dir := L.CheckString(1) + + + git.PlainClone("/tmp", &git.CloneOptions{}) + + depth := 1 + if L.GetTop() > 1 { + depth = L.CheckInt(2) + } + + repo, err := git.PlainOpen(dir) + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + worktree, err := repo.Worktree() + if err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + if err := worktree.Pull(&git.PullOptions{Depth: depth}); err != nil { + L.Push(lua.LFalse) + L.Push(lua.LString(err.Error())) + return 2 + } + + L.Push(lua.LTrue) + L.Push(lua.LNil) + return 2 +} + +func tryCheckout(worktree git.Worktree, reference string) error { + err := worktree.Checkout(&git.CheckoutOptions{ + Branch: plumbing.ReferenceName(reference), + }) + if err == nil { + return nil + } + + err = worktree.Checkout(&git.CheckoutOptions{ + Branch: plumbing.ReferenceName("refs/heads/" + reference), + }) + if err == nil { + return nil + } + + err = worktree.Checkout(&git.CheckoutOptions{ + Branch: plumbing.ReferenceName("refs/tags/" + reference), + }) + if err == nil { + return nil + } + + hash := plumbing.NewHash(reference) + err = worktree.Checkout(&git.CheckoutOptions{ + Hash: hash, + }) + if err == nil { + return nil + } + + return fmt.Errorf("cannot checkout '%s' as branch, tag, or commit", reference) +} diff --git a/internal/utils/lua/lua.go b/internal/utils/lua/lua.go index 425582a..6e5b663 100644 --- a/internal/utils/lua/lua.go +++ b/internal/utils/lua/lua.go @@ -14,16 +14,12 @@ func GetSandBox() (lua.LState, error) { } L := lua.NewState() osObject := L.GetGlobal("os").(*lua.LTable) - L.SetGlobal("SAFE_MODE", lua.LTrue) - L.SetGlobal("PACKETS_DATADIR", lua.LString(cfg.Config.Data_d)) - L.SetGlobal("packets_bin_dir", lua.LString(cfg.Config.Bin_d)) + L.SetGlobal("BIN_DIR", lua.LString(cfg.Config.Bin_d)) L.SetGlobal("path_join", L.NewFunction(Ljoin)) - // Packets build functions - osObject.RawSetString("remove", L.NewFunction(LSafeRemove)) osObject.RawSetString("rename", L.NewFunction(LSafeRename)) osObject.RawSetString("copy", L.NewFunction(LSafeCopy)) diff --git a/internal/utils/lua/luafunctions.go b/internal/utils/lua/luafunctions.go index cab3c8d..a8378ff 100644 --- a/internal/utils/lua/luafunctions.go +++ b/internal/utils/lua/luafunctions.go @@ -253,8 +253,8 @@ func LError(L *lua.LState) int { parts = append(parts, val.String()) } - Llogger().Panic(parts...) + llogger().Panic(parts...) return 0 } -func Llogger() *log.Logger { return log.New(os.Stderr, " script error: ", 0) } +func llogger() *log.Logger { return log.New(os.Stderr, " script error: ", 0) } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 691293b..97280fe 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,8 +1,6 @@ package utils import ( - "archive/tar" - "bytes" "crypto/ed25519" "database/sql" "fmt" @@ -17,12 +15,9 @@ import ( "strings" "syscall" - "packets/configs" "packets/internal/consts" errors_packets "packets/internal/errors" - - "github.com/klauspost/compress/zstd" - "github.com/pelletier/go-toml/v2" + "packets/internal/packet" ) type Package struct { @@ -39,11 +34,11 @@ type Package struct { Size int64 Dependencies map[string]string + Manifest packet.PacketLua + Signature []byte PublicKey ed25519.PublicKey - Manifest configs.Manifest - Serial int } @@ -65,40 +60,6 @@ func GetFileHTTP(url string) ([]byte, error) { return fileBytes, nil } -// ReadManifest is crucial to get package metadata it reads manifest.toml from a package file (tar.zst) -func ReadManifest(file io.Reader) (configs.Manifest, error) { - zstdReader, err := zstd.NewReader(file) - if err != nil { - return configs.Manifest{}, err - } - defer zstdReader.Close() - - tarReader := tar.NewReader(zstdReader) - for { - header, err := tarReader.Next() - if err == io.EOF { - break - } - if err != nil { - return configs.Manifest{}, err - } - - if filepath.Base(header.Name) == "manifest.toml" { - decoder := toml.NewDecoder(tarReader) - - var manifest configs.Manifest - - if err := decoder.Decode(&manifest); err != nil { - return configs.Manifest{}, nil - } - - return manifest, nil - } - - } - return configs.Manifest{}, errors_packets.ErrCantFindManifestTOML -} - // CopyDir copies a directory from source to destination func CopyDir(src string, dest string) error { if stats, err := os.Stat(src); err != nil { @@ -190,6 +151,7 @@ func CopyFile(source string, destination string) error { } // Write writes the package file to the cache directory and returns the path to it + func (p *Package) Write() (string, error) { if err := os.WriteFile(filepath.Join(consts.DefaultCache_d, p.Filename), p.PackageF, 0o644); err != nil { _ = os.Remove(filepath.Join(consts.DefaultCache_d, p.Filename)) @@ -210,7 +172,7 @@ func (p *Package) AddToInstalledDB(inCache int, packagePath string) error { defer func() { if !success { - _, err := db.Exec("DELETE FROM packages WHERE id = ?", p.Manifest.Info.Id) + _, err := db.Exec("DELETE FROM packages WHERE id = ?", p.Manifest.Id) if err != nil { log.Println("failed to rollback package addition:", err) } @@ -218,12 +180,12 @@ func (p *Package) AddToInstalledDB(inCache int, packagePath string) error { }() _, err = db.Exec(` - INSERT INTO packages ( - query_name, id, version, description, - serial, package_d, filename, os, arch, in_cache - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + INSERT INTO packages ( + query_name, id, version, description, + serial, package_d, filename, os, arch, in_cache + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, p.QueryName, - p.Manifest.Info.Id, + p.Manifest.Id, p.Version, p.Description, p.Serial, @@ -238,7 +200,7 @@ func (p *Package) AddToInstalledDB(inCache int, packagePath string) error { } for depnName, versionConstraint := range p.Dependencies { - _, err = db.Exec("INSERT INTO package_dependencies (package_id, dependency_name, version_constraint) VALUES (?, ?, ?)", p.Manifest.Info.Id, depnName, versionConstraint) + _, err = db.Exec("INSERT INTO package_dependencies (package_id, dependency_name, version_constraint) VALUES (?, ?, ?)", p.Manifest.Id, depnName, versionConstraint) } success = true @@ -302,6 +264,9 @@ func ResolvDependencies(depnList map[string]string) ([]string, error) { value := strings.TrimLeft(constraint, "<>=") switch { + case constraint == "any": + filter = "" + order = "ORDER BY serial DESC LIMIT 1" case strings.HasPrefix(constraint, ">"): filter = fmt.Sprintf("AND serial > %s", value) order = "ORDER BY serial DESC LIMIT 1" @@ -343,18 +308,6 @@ func ResolvDependencies(depnList map[string]string) ([]string, error) { return resolved, nil } -func ManifestFileRead(file io.Reader) (configs.Manifest, error) { - decoder := toml.NewDecoder(file) - - var manifest configs.Manifest - - if err := decoder.Decode(&manifest); err != nil { - return configs.Manifest{}, nil - } - - return manifest, nil -} - func RemoveFromInstalledDB(id string) error { db, err := sql.Open("sqlite", consts.InstalledDB) if err != nil { @@ -468,8 +421,7 @@ func GetPackage(id string) (Package, error) { skipping: - reader := bytes.NewReader(this.PackageF) - this.Manifest, err = ReadManifest(reader) + this.Manifest, err = packet.ReadPacket(this.PackageF) if err != nil { return Package{}, err } @@ -482,7 +434,7 @@ skipping: } func GetPacketsUID() (int, error) { - _ = exec.Command("useradd", "-M", "-N", "-r", "-s", "/bin/false", "-d", "/var/lib/packets", "packets").Run() + _ = exec.Command("useradd", "-M", "-N", "-r", "-s", "/bin/false", "-d", "/etc/packets", "packets").Run() cmd := exec.Command("id", "-u", "packets") out, err := cmd.CombinedOutput() @@ -510,3 +462,21 @@ func ChangeToNoPermission() error { } func ElevatePermission() error { return syscall.Setresuid(0, 0, 0) } + +func getFileHTTP(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + + if resp.StatusCode != http.StatusOK { + return nil, errors_packets.ErrResponseNot200OK + } + + fileBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return fileBytes, nil +} diff --git a/pkg/lan.go b/pkg/lan.go new file mode 100644 index 0000000..37cdf9d --- /dev/null +++ b/pkg/lan.go @@ -0,0 +1,80 @@ +package packets + +import ( + "fmt" + "net" + "packets/internal/consts" + "strconv" + "strings" + "time" + + "golang.org/x/net/ipv4" +) + +type Peer struct { + IP net.IP + Port int +} + +func BroadcastAddr(ip net.IP, mask net.IPMask) net.IP { + b := make(net.IP, len(ip)) + for i := range ip { + b[i] = ip[i] | ^mask[i] + } + return b +} + +func AskLAN(filename string) ([]Peer, error) { + var peers []Peer + query := []byte("Q:" + filename) + + pc, err := net.ListenPacket("udp", ":0") + if err != nil { + return []Peer{}, err + } + defer pc.Close() + + if pconn := ipv4.NewPacketConn(pc); pconn != nil { + _ = pconn.SetTTL(1) + } + + ifaces, _ := net.Interfaces() + for _, ifc := range ifaces { + if ifc.Flags&net.FlagUp == 0 || ifc.Flags&net.FlagLoopback != 0 { + continue + } + + addrs, _ := ifc.Addrs() + for _, a := range addrs { + ipnet, ok := a.(*net.IPNet) + if !ok || ipnet.IP.To4() == nil { + continue + } + + bcast := BroadcastAddr(ipnet.IP.To4(), ipnet.Mask) + dst := &net.UDPAddr{IP: bcast, Port: 1333} + + _, err = pc.WriteTo(query, dst) + if err != nil { + fmt.Printf(":: (%s) can't send to %s: %s\n", ifc.Name, bcast, err.Error()) + } + } + } + _ = pc.SetDeadline(time.Now().Add(consts.LANDeadline)) + buf := make([]byte, 1500) + + for { + n, addr, err := pc.ReadFrom(buf) + if err != nil { + break + } + msg := string(buf[:n]) + + if strings.HasPrefix(msg, "H:"+filename) { + parts := strings.Split(msg, ":") + port, _ := strconv.Atoi(parts[2]) + peers = append(peers, Peer{IP: addr.(*net.UDPAddr).IP, Port: port}) + } + } + return peers, nil +} diff --git a/pkg/main.go b/pkg/main.go index b016e50..7d3e379 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -3,23 +3,90 @@ package packets import ( "archive/tar" "bytes" + "crypto/ed25519" + "database/sql" + "fmt" "io" + "log" + "net/http" "os" + "packets/internal/build" + "packets/internal/consts" + errors_packets "packets/internal/errors" + "packets/internal/packet" "packets/internal/utils" - "runtime" + "path" utils_lua "packets/internal/utils/lua" "path/filepath" "strings" "github.com/klauspost/compress/zstd" - lua "github.com/yuin/gopher-lua" _ "modernc.org/sqlite" ) +type Package struct { + PackageF []byte + Version string + ImageUrl string + QueryName string + Description string + Author string + AuthorVerified bool + OS string + Arch string + Filename string + Size int64 + Dependencies map[string]string + + Signature []byte + PublicKey ed25519.PublicKey + + Serial int + + Manifest packet.PacketLua +} + // Install exctract and fully install from a package file ( tar.zst ) func InstallPackage(file []byte, destDir string) error { - manifest, err := utils.ReadManifest(bytes.NewReader(file)) + + packetLua, err := packet.ReadPacket(file) + if err == nil { + L, err := utils_lua.GetSandBox() + if err != nil { + return err + } + + bootstrapcontainer, err := build.NewContainer(packetLua) + if err != nil { + return err + } + + os.Chdir(destDir) + + if err := utils.ChangeToNoPermission(); err != nil { + return fmt.Errorf("error changing to packet user: %s", err) + } + + if err := bootstrapcontainer.ExecutePrepare(packetLua, &L); err != nil { + return fmt.Errorf("error executing prepare: %s", err) + } + + if err := bootstrapcontainer.ExecuteBuild(packetLua, &L); err != nil { + return fmt.Errorf("error executing build: %s", err) + } + + if err := utils.ElevatePermission(); err != nil { + return fmt.Errorf("error changing to root: %s", err) + } + + if err := bootstrapcontainer.ExecuteInstall(packetLua, &L); err != nil { + return fmt.Errorf("error executing build: %s", err) + } + return nil + } + + manifest, err := packet.ReadPacketFromFile(bytes.NewReader(file)) if err != nil { return err } @@ -96,7 +163,7 @@ func InstallPackage(file []byte, destDir string) error { return err } - if filepath.Base(hdr.Name) == "manifest.toml" || filepath.Base(hdr.Name) == manifest.Hooks.Install || filepath.Base(hdr.Name) == manifest.Hooks.Remove { + if filepath.Base(hdr.Name) == "Packet.lua" { err = os.Chmod(absPath, os.FileMode(0755)) if err != nil { return err @@ -113,52 +180,175 @@ func InstallPackage(file []byte, destDir string) error { if err != nil { return err } - L.SetGlobal("data_dir", lua.LString(filepath.Join(destDir, "data"))) - L.SetGlobal("script", lua.LString(manifest.Hooks.Install)) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if err := utils.ChangeToNoPermission(); err != nil { - return err - } - if err := L.DoFile(filepath.Join(destDir, manifest.Hooks.Install)); err != nil { - return err - } - - if err := utils.ElevatePermission(); err != nil { - return err - } - - return nil -} - -// ExecuteRemoveScript executes the remove script from the package -func ExecuteRemoveScript(path string) error { - - L, err := utils_lua.GetSandBox() + bootstrapcontainer, err := build.NewContainer(manifest) if err != nil { return err } - L.SetGlobal("data_dir", lua.LFalse) - L.SetGlobal("script", lua.LString(path)) - L.SetGlobal("build", lua.LNil) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() + os.Chdir(destDir) if err := utils.ChangeToNoPermission(); err != nil { - return err + return fmt.Errorf("error changing to packet user: %s", err) } - if err := L.DoFile(path); err != nil { - return err + if err := bootstrapcontainer.ExecutePrepare(manifest, &L); err != nil { + return fmt.Errorf("error executing prepare: %s", err) + } + + if err := bootstrapcontainer.ExecuteBuild(manifest, &L); err != nil { + return fmt.Errorf("error executing build: %s", err) } if err := utils.ElevatePermission(); err != nil { - return err + return fmt.Errorf("error changing to root: %s", err) + } + + if err := bootstrapcontainer.ExecuteInstall(manifest, &L); err != nil { + return fmt.Errorf("error executing build: %s", err) } return nil } + +func GetPackage(id string) (Package, error) { + + var this Package + this.Dependencies = make(map[string]string) + var peers []Peer + + db, err := sql.Open("sqlite", consts.IndexDB) + if err != nil { + return this, err + } + defer db.Close() + + var packageUrl, typePackage string + err = db.QueryRow("SELECT query_name, version, package_url, image_url, description, author, author_verified, os, arch, signature, public_key, serial, size, type FROM packages WHERE id = ?", id). + Scan( + &this.QueryName, + &this.Version, + &packageUrl, + &this.ImageUrl, + &this.Description, + &this.Author, + &this.AuthorVerified, + &this.OS, + &this.Arch, + &this.Signature, + &this.PublicKey, + &this.Serial, + &this.Size, + &typePackage, + ) + if err != nil { + return Package{}, err + } + + rows, err := db.Query("SELECT dependency_name, version_constraint FROM package_dependencies WHERE package_id = ?", id) + if err != nil { + return Package{}, err + } + defer rows.Close() + + for rows.Next() { + var a, vConstraint string + if err := rows.Scan(&a, &vConstraint); err != nil { + return Package{}, err + } + + this.Dependencies[a] = vConstraint + } + + if strings.Contains(typePackage, " ") { + + filename := path.Base(packageUrl) + this.Filename = filename + + dirEntry, err := os.ReadDir(consts.DefaultCache_d) + if err != nil { + return Package{}, err + } + + for _, v := range dirEntry { + if v.Name() == filename { + this.PackageF, err = os.ReadFile(filepath.Join(consts.DefaultCache_d, filename)) + if err != nil { + break + } + goto skipping + + } + } + + peers, err = AskLAN(filename) + if err != nil { + return Package{}, err + } + + if len(peers) == 0 { + fmt.Printf(":: Pulling from %s\n", packageUrl) + this.PackageF, err = getFileHTTP(packageUrl) + if err != nil { + return Package{}, err + } + } else { + var totalerrors int = 0 + for _, peer := range peers { + fmt.Printf(":: Pulling from local network (%s)\n", peer.IP) + this.PackageF, err = getFileHTTP(fmt.Sprintf("http://%s:%d/%s", peer.IP, peer.Port, filename)) + if err == nil { + break + } else { + totalerrors++ + } + } + if totalerrors == len(peers) { + this.PackageF, err = getFileHTTP(packageUrl) + if err != nil { + return Package{}, err + } + } + } + } else { + filds := strings.Fields(typePackage) + pkt, err := packet.GetPackageDotLuaFromRemote(filds[0], filds[1]) + if err != nil { + return Package{}, err + } + this.Manifest = pkt + return this, nil + } + +skipping: + + reader := bytes.NewReader(this.PackageF) + this.Manifest, err = packet.ReadPacketFromFile(reader) + if err != nil { + return Package{}, err + } + + if !ed25519.Verify(this.PublicKey, this.PackageF, this.Signature) { + return Package{}, errors_packets.ErrInvalidSignature + } + + return this, nil +} + +func getFileHTTP(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + + if resp.StatusCode != http.StatusOK { + return nil, errors_packets.ErrResponseNot200OK + } + + fileBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return fileBytes, nil +} diff --git a/systemd/packets-http.service b/systemd/packets-http.service new file mode 100644 index 0000000..e7f7eb1 --- /dev/null +++ b/systemd/packets-http.service @@ -0,0 +1,14 @@ +[Unit] + Description=Serving packages for everyone in the same network. + After=network.target packets-udp.service + Requires=packets-udp.service + +[Service] + Type=simple + User=packets + WorkingDirectory=/var/cache/packets + ExecStart=/etc/packets/httpsocket + Restart=always + +[Install] + WantedBy=multi-user.target \ No newline at end of file diff --git a/systemd/packets-udp.service b/systemd/packets-udp.service new file mode 100644 index 0000000..0d0382c --- /dev/null +++ b/systemd/packets-udp.service @@ -0,0 +1,14 @@ +[Unit] + Description=Respond to every request for packages in local network. + After=network.target + +[Service] + Type=simple + User=packets + WorkingDirectory=/etc/packets + ExecStart=/etc/packets/udpsocket + Restart=always + + +[Install] + WantedBy=multi-user.target \ No newline at end of file