aboutsummaryrefslogtreecommitdiff
path: root/removepkg.go
diff options
context:
space:
mode:
Diffstat (limited to 'removepkg.go')
-rw-r--r--removepkg.go278
1 files changed, 278 insertions, 0 deletions
diff --git a/removepkg.go b/removepkg.go
new file mode 100644
index 0000000..a1fab5e
--- /dev/null
+++ b/removepkg.go
@@ -0,0 +1,278 @@
+package pkgtools
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/juju/fslock"
+ "github.com/pkg/errors"
+)
+
+const UninstallScript = "var/lib/pkgtools/douinst.sh"
+
+func getPackageName(pkg string) string {
+ return PackageBase(pkg)
+}
+
+// Official flags
+// [ ROOT=/mnt ] removepkg [--copy] [--keep] [--preserve] [--skip-douninst] [--terse] [--warn] packagename ...
+
+type RemovePkgFlags struct {
+ LockDir string
+ Root string
+}
+
+var DefaultRemovePkgFlags = RemovePkgFlags{
+ LockDir: InstallLockDir,
+ Root: "/",
+}
+
+func (s *RemovePkgFlags) 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 guessPackagename(root string, str string) (string, bool) {
+ // TODO: check if package exists
+ return str, true
+}
+
+func runUninstallScript(root string) error {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+ err = os.Chdir(root)
+ if err != nil {
+ return err
+ }
+ defer os.Chdir(cwd)
+
+ uninstallScript := path.Join(root, PackageUninstallScript)
+ if _, err := os.Stat(uninstallScript); !os.IsNotExist(err) {
+ cmd := exec.Command("/bin/sh", uninstallScript)
+ cmd.Stdin = os.Stdout
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func runLDConfig(lockdir string) error {
+ l := fslock.New(FileLockPath(lockdir, "ldconfig"))
+ if err := l.Lock(); err != nil {
+ return err
+ }
+ defer l.Unlock()
+
+ if _, err := os.Stat("/sbin/ldconfig"); !os.IsNotExist(err) {
+ cmd := exec.Command("/sbin/ldconfig")
+ cmd.Stdin = os.Stdout
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = io.Discard
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+func deletePackageFiles(
+ flags *RemovePkgFlags,
+ pkg string,
+) error {
+ info, err := GetPackageInfo(flags.Root, pkg)
+ if err != nil {
+ return err
+ }
+ pkgFiles := info.FileList
+ links, err := GetPackageSoftLinks(flags.Root, pkg)
+ if err != nil {
+ return err
+ }
+ pkgFiles = append(pkgFiles, links...)
+ pkgFiles = append(pkgFiles, catPaths(pkgFiles)...)
+
+ // get all other package files
+ otherPkgFiles := make(map[string]bool)
+ otherPkgs, err := ListPackages(flags.Root)
+ if err != nil {
+ return err
+ }
+ for _, o := range otherPkgs {
+ if o == pkg {
+ continue
+ }
+ info, err := GetPackageInfo(flags.Root, o)
+ if err != nil {
+ return err
+ }
+ paths := info.FileList
+
+ links, err := GetPackageSoftLinks(flags.Root, o)
+ paths = append(paths, links...)
+
+ paths = append(paths, catPaths(paths)...)
+ for _, p := range paths {
+ otherPkgFiles[p] = true
+ }
+ }
+
+ var dirs []string
+ for _, fp := range pkgFiles {
+ if ok := otherPkgFiles[fp]; ok {
+ continue
+ }
+
+ tfp := filepath.Join(flags.Root, fp)
+ if ok, _ := IsDir(tfp); ok {
+ dirs = append(dirs, fp)
+ continue
+ }
+
+ // Bravely attempt removal ignoring errors. The os.Stat
+ // returns a NotExist error soft links whose target is missing,
+ // resulting in error.
+ _ = os.Remove(tfp)
+ }
+
+ sort.Sort(sort.Reverse(sort.StringSlice(dirs)))
+ for _, fp := range dirs {
+ tfp := filepath.Join(flags.Root, fp)
+ // Remove deletes only empty directories
+ _ = os.Remove(tfp)
+ }
+
+ return nil
+}
+
+func catPaths(fps []string) []string {
+ var cats []string
+ for _, p := range fps {
+ if !strings.HasPrefix(p, "usr/man/man") {
+ continue
+ }
+ cat := strings.Replace(p, "usr/man/man", "usr/man/cat", -1)
+ cats = append(cats, cat)
+ }
+ return cats
+}
+
+func RemovePkg(
+ flags *RemovePkgFlags,
+ pkgNames ...string,
+) error {
+ // TODO: Apply default flag values
+
+ err := os.MkdirAll(flags.LockDir, DefaultPerms)
+ if err != nil {
+ return err
+ }
+
+ tmpDir := TargetTmpDir(flags.Root)
+ if ok, _ := IsDir(tmpDir); !ok {
+ err := os.MkdirAll(tmpDir, DefaultPerms)
+ if err != nil {
+ return err
+ }
+ err = os.Chmod(tmpDir, TmpDirPerms)
+ if err != nil {
+ return err
+ }
+ }
+
+ var pkgs []string
+ for _, pkgName := range pkgNames {
+ pkg, ok, err := GetFullPackageName(flags.Root, pkgName)
+ if err != nil {
+ return errors.Wrap(err, "getting package information")
+ }
+ if !ok {
+ return errors.Errorf("package does not exist '%s'", pkgName)
+ }
+ pkgs = append(pkgs, pkg)
+ }
+
+ for _, pkg := range pkgs {
+ fmt.Printf("removing %s\n", pkg)
+
+ tmpUninstallScript := ""
+ if ok, _ := IsFile(filepath.Join(flags.Root, UninstallScript, pkg)); ok {
+ tmpUninstallScript = filepath.Join(tmpDir, pkg)
+ err := CopyFile(
+ tmpUninstallScript,
+ filepath.Join(flags.Root, UninstallScript, pkg),
+ )
+ if err != nil {
+ return errors.Wrap(err, "removing package "+pkg)
+ }
+ }
+
+ err := deletePackageFiles(flags, pkg)
+ if err != nil {
+ // TODO: should removepkg bail out on remove error
+ return errors.Wrap(err, "deleting package files")
+ }
+
+ err = runUninstallScript(flags.Root)
+ if err != nil {
+ return err
+ }
+
+ err = os.Rename(
+ TargetPackageInfoPath(flags.Root, pkg),
+ filepath.Join(flags.Root, InstalledRemovedPackagePath, pkg),
+ )
+ if err != nil {
+ return errors.Wrap(err, "removing package "+pkg)
+ }
+ if ok, _ := IsFile(TargetScriptPath(flags.Root, pkg)); ok {
+ err := os.Rename(
+ TargetScriptPath(flags.Root, pkg),
+ filepath.Join(flags.Root, InstalledRemovedScriptsPath, pkg),
+ )
+ if err != nil {
+ return errors.Wrap(err, "removing package "+pkg)
+ }
+ }
+ if ok, _ := IsFile(filepath.Join(tmpDir, pkg)); ok {
+ err := os.Rename(
+ tmpUninstallScript,
+ TargetRemovedUninstalllScriptPath(flags.Root, pkg),
+ )
+ if err != nil {
+ return errors.Wrap(err, "removing package "+pkg)
+ }
+ }
+
+ if flags.Root == "/" {
+ err = runLDConfig(flags.LockDir)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}