diff options
author | Slack Coder <slackcoder@server.ky> | 2022-03-11 13:08:24 -0500 |
---|---|---|
committer | Slack Coder <slackcoder@server.ky> | 2022-03-30 11:34:46 -0500 |
commit | 29589a24b13fb223b113e94eca2c4fff0e56a4d9 (patch) | |
tree | e1754d195463439ae2834cd502b170648e47cdb8 /installpkg.go | |
download | pkgtools-go-29589a24b13fb223b113e94eca2c4fff0e56a4d9.tar.xz |
Initial commit
Diffstat (limited to 'installpkg.go')
-rw-r--r-- | installpkg.go | 347 |
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 +} |