aboutsummaryrefslogtreecommitdiffsponsor
path: root/installpkg.go
diff options
context:
space:
mode:
authorSlack Coder <slackcoder@server.ky>2022-03-11 13:08:24 -0500
committerSlack Coder <slackcoder@server.ky>2022-03-30 11:34:46 -0500
commit29589a24b13fb223b113e94eca2c4fff0e56a4d9 (patch)
treee1754d195463439ae2834cd502b170648e47cdb8 /installpkg.go
downloadpkgtools-go-29589a24b13fb223b113e94eca2c4fff0e56a4d9.tar.xz
Initial commit
Diffstat (limited to 'installpkg.go')
-rw-r--r--installpkg.go347
1 files changed, 347 insertions, 0 deletions
diff --git a/installpkg.go b/installpkg.go
new file mode 100644
index 0000000..7933960
--- /dev/null
+++ b/installpkg.go
@@ -0,0 +1,347 @@
+package pkgtools
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "time"
+
+ "github.com/juju/fslock"
+ "github.com/pkg/errors"
+)
+
+const umask = "022"
+
+const DefaultPerms = 0755
+
+// ExitStatusTarError is when tar returned error code.
+const ExitStatusTarError = 1
+
+// ExitStatusCorruptCompression is when corrupt compression envelope.
+const ExitStatusCorruptCompression = 2
+
+// ExitStatusIncorrectExt is when does not end in .tgz.
+const ExitStatusIncorrectExt = 3
+
+// ExitStatusNoSuchFile is when no such file.
+const ExitStatusNoSuchFile = 4
+
+// ExitStatusMissingCompressionUtility is when external compression utility.
+// missing
+const ExitStatusMissingCompressionUtility = 5
+
+// ExitStatusUserAbortFromMenu is when user abort from menu mode.
+const ExitStatusUserAbortFromMenu = 99
+
+var ErrNotImplemented = errors.New("not implemented")
+
+// DefaultTerseLength is the default line length during terse mode.
+const DefaultTerseLength = 80
+
+// Ended at line 232
+
+const (
+ PriorityNone = ""
+ PriorityAdditional = "ADD"
+ PriorityRecommended = "REC"
+ PriorityOptional = "OPT"
+ PrioritySkip = "SKP"
+)
+
+type InstallPkgFlags struct {
+ Ask bool
+ InfoBox bool
+ LockDir string
+ MD5Sum bool
+ Menu bool
+ NoOverwrite bool
+ Priority string
+ Root string
+ TagFile string
+ Terse bool
+ TerseLength int
+ Warn bool
+
+ chown bool
+ chmod bool
+}
+
+var DefaultInstallPkgFlags = InstallPkgFlags{
+ Ask: false,
+ InfoBox: false,
+ LockDir: InstallLockDir,
+ MD5Sum: false,
+ Menu: false,
+ NoOverwrite: false,
+ Priority: PriorityNone,
+ Root: "/",
+ TagFile: "",
+ Terse: false,
+ TerseLength: DefaultTerseLength,
+ Warn: false,
+
+ chown: true,
+ chmod: true,
+}
+
+func (s *InstallPkgFlags) SetEnvValues() {
+ if v := os.Getenv("INSTLOCKDIR"); v != "" {
+ s.LockDir = v
+ }
+
+ // Official installpkg prefers the argument of the environment
+ // variable.
+ if v := os.Getenv("ROOT"); v != "" {
+ s.Root = v
+ }
+}
+
+func runInstallScript(flags *InstallPkgFlags) error {
+ l := fslock.New(FileLockPath(flags.LockDir, filepath.Base(PackageInstallScript)))
+ if err := l.Lock(); err != nil {
+ return err
+ }
+ defer l.Unlock()
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+ err = os.Chdir(flags.Root)
+ if err != nil {
+ return err
+ }
+ defer os.Chdir(cwd)
+
+ installScript := path.Join(flags.Root, PackageInstallScript)
+ if _, err := os.Stat(installScript); !os.IsNotExist(err) {
+ cmd := exec.Command("/bin/bash", installScript)
+ cmd.Stdin = os.Stdout
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+const ADMDirPerms = 0755
+
+const LogDirPerms = 0755
+
+const TmpDirPerms = 0700
+
+func initializeDirectories(flags *InstallPkgFlags) error {
+ err := os.MkdirAll(flags.LockDir, DefaultPerms)
+ if err != nil {
+ return err
+ }
+
+ dirs := []string{
+ "douninst.sh",
+ "packages",
+ "scripts",
+ "setup",
+ }
+ for _, f := range dirs {
+ fp := filepath.Join(flags.Root, ADMDir, f)
+ err := os.MkdirAll(fp, DefaultPerms)
+ if err != nil {
+ return err
+ }
+ err = os.Chmod(fp, ADMDirPerms)
+ if err != nil {
+ return err
+ }
+ }
+
+ dirs = []string{
+ "removed_packages",
+ "removed_scripts",
+ }
+ for _, f := range dirs {
+ fp := filepath.Join(flags.Root, LogDir, f)
+ if ok, err := IsDir(fp); !ok && !os.IsNotExist(err) {
+ err := os.Remove(fp)
+ if err != nil {
+ return err
+ }
+ }
+
+ err := os.MkdirAll(fp, DefaultPerms)
+ if err != nil {
+ return err
+ }
+ err = os.Chmod(fp, LogDirPerms)
+ if err != nil {
+ return err
+ }
+ }
+
+ dirs = []string{
+ "packages",
+ "scripts",
+ "setup",
+ }
+ for _, f := range dirs {
+ fp := filepath.Join(flags.Root, LogDir, f)
+ isDir, _ := IsDir(fp)
+ isSymlink, _ := IsSymlink(fp)
+ if isDir || isSymlink {
+ continue
+ }
+
+ err := os.Symlink(filepath.Join("..", "..", "lib", "pkgtools", f), fp)
+ if err != nil {
+ return err
+ }
+ }
+
+ fp := filepath.Join(flags.Root, TmpDir)
+ if ok, _ := IsDir(TmpDir); !ok {
+ err := os.MkdirAll(fp, DefaultPerms)
+ if err != nil {
+ return err
+ }
+
+ err = os.Chmod(fp, TmpDirPerms)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func showSlackDesc(target string) error {
+ return nil
+}
+
+// TODO: Check permissions for written files
+func writeToDatabase(target string, pkg string) error {
+ // copy install script
+ // TODO: include uninstall script
+ installScript := filepath.Join(target, ADMDir, "scripts", PackageBase(pkg))
+ uninstallScript := filepath.Join(target, ADMDir, "douninst.sh", PackageBase(pkg))
+ slackPkg, err := OpenSlackwarePkg(pkg)
+ if err != nil {
+ return err
+ }
+ defer slackPkg.Close()
+ for {
+ entry, err := slackPkg.Next()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return err
+ }
+ if entry == nil {
+ continue
+ }
+
+ switch entry.Name {
+ case PackageInstallScript:
+ f, err := os.Create(installScript)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = io.Copy(f, slackPkg)
+ if err != nil {
+ return err
+ }
+ case PackageUninstallScript:
+ f, err := os.Create(uninstallScript)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = io.Copy(f, slackPkg)
+ if err != nil {
+ return err
+ }
+ default:
+ _, _ = io.Copy(io.Discard, slackPkg)
+ }
+ }
+
+ fp := filepath.Join(target, ADMDir, "packages", PackageBase(pkg))
+ f, err := os.Create(fp)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ e := NewEncoder(f)
+ err = e.Encode(slackPkg.PkgInfo())
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func InstallPkg(flags *InstallPkgFlags, pkgs ...string) error {
+ // Apply default flag values
+
+ err := initializeDirectories(flags)
+ if err != nil {
+ return err
+ }
+
+ for _, pkg := range pkgs {
+ fmt.Printf("installing %s\n", PackageBase(pkg))
+ err = writeToDatabase(flags.Root, pkg)
+ if err != nil {
+ return errors.Wrap(err, "writing package to database")
+ }
+
+ err = func() error {
+ slackPkg, err := OpenSlackwarePkg(pkg)
+ if err != nil {
+ return err
+ }
+ defer slackPkg.Close()
+ err = InstallArchive(
+ slackPkg,
+ &InstallArchiveCfg{
+ Root: flags.Root,
+ Chmod: flags.chmod,
+ Chown: flags.chown,
+ },
+ )
+ return errors.Wrap(err, "installing package")
+ }()
+ if err != nil {
+ return errors.Wrap(err, "writing package to database")
+ }
+
+ _ = os.Chtimes(
+ filepath.Join(flags.Root, ADMDir, "packages", PackageBase(pkg)),
+ time.Now(),
+ time.Now(),
+ )
+
+ _ = runLDConfig(flags.LockDir)
+
+ // TODO: command should still run after failed 'install script' execution
+ err = runInstallScript(flags)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, errors.Wrap(err, "running install script"))
+ }
+
+ installPath := path.Join(flags.Root, PackageInstallPath)
+ err = os.RemoveAll(installPath)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}