yay/pkg/db/ialpm/alpm.go

507 lines
11 KiB
Go
Raw Normal View History

2020-08-16 21:41:38 +00:00
package ialpm
2020-07-28 23:53:25 +00:00
import (
"errors"
2020-08-08 16:43:37 +00:00
"fmt"
"os"
2020-08-16 21:41:38 +00:00
"strconv"
2020-08-08 16:43:37 +00:00
"time"
2020-10-01 11:38:03 +00:00
alpm "github.com/Jguer/go-alpm/v2"
2020-08-07 16:55:19 +00:00
pacmanconf "github.com/Morganamilo/go-pacmanconf"
"github.com/leonelquinteros/gotext"
2021-09-08 20:28:08 +00:00
"github.com/Jguer/yay/v11/pkg/db"
"github.com/Jguer/yay/v11/pkg/settings"
"github.com/Jguer/yay/v11/pkg/text"
"github.com/Jguer/yay/v11/pkg/upgrade"
2020-07-28 23:53:25 +00:00
)
type AlpmExecutor struct {
handle *alpm.Handle
localDB alpm.IDB
syncDB alpm.IDBList
syncDBsCache []alpm.IDB
conf *pacmanconf.Config
2020-07-28 23:53:25 +00:00
}
2020-10-26 08:10:34 +00:00
func NewExecutor(pacmanConf *pacmanconf.Config) (*AlpmExecutor, error) {
ae := &AlpmExecutor{conf: pacmanConf}
2020-08-08 16:43:37 +00:00
err := ae.RefreshHandle()
2020-07-28 23:53:25 +00:00
if err != nil {
return nil, err
}
2020-08-08 16:43:37 +00:00
ae.localDB, err = ae.handle.LocalDB()
2020-07-28 23:53:25 +00:00
if err != nil {
return nil, err
}
2020-08-08 16:43:37 +00:00
ae.syncDB, err = ae.handle.SyncDBs()
if err != nil {
return nil, err
}
return ae, nil
}
func toUsage(usages []string) alpm.Usage {
if len(usages) == 0 {
return alpm.UsageAll
}
var ret alpm.Usage
2021-08-11 18:13:28 +00:00
for _, usage := range usages {
switch usage {
case "Sync":
ret |= alpm.UsageSync
case "Search":
ret |= alpm.UsageSearch
case "Install":
ret |= alpm.UsageInstall
case "Upgrade":
ret |= alpm.UsageUpgrade
case "All":
ret |= alpm.UsageAll
}
}
return ret
}
func configureAlpm(pacmanConf *pacmanconf.Config, alpmHandle *alpm.Handle) error {
for _, repo := range pacmanConf.Repos {
// TODO: set SigLevel
2020-08-16 21:41:38 +00:00
alpmDB, err := alpmHandle.RegisterSyncDB(repo.Name, 0)
if err != nil {
return err
}
2020-08-16 21:41:38 +00:00
alpmDB.SetServers(repo.Servers)
alpmDB.SetUsage(toUsage(repo.Usage))
}
if err := alpmHandle.SetCacheDirs(pacmanConf.CacheDir); err != nil {
return err
}
// add hook directories 1-by-1 to avoid overwriting the system directory
for _, dir := range pacmanConf.HookDir {
if err := alpmHandle.AddHookDir(dir); err != nil {
return err
}
}
if err := alpmHandle.SetGPGDir(pacmanConf.GPGDir); err != nil {
return err
}
if err := alpmHandle.SetLogFile(pacmanConf.LogFile); err != nil {
return err
}
if err := alpmHandle.SetIgnorePkgs(pacmanConf.IgnorePkg); err != nil {
return err
}
if err := alpmHandle.SetIgnoreGroups(pacmanConf.IgnoreGroup); err != nil {
return err
}
if err := alpmSetArchitecture(alpmHandle, pacmanConf.Architecture); err != nil {
return err
}
if err := alpmHandle.SetNoUpgrades(pacmanConf.NoUpgrade); err != nil {
return err
}
if err := alpmHandle.SetNoExtracts(pacmanConf.NoExtract); err != nil {
return err
}
if err := alpmHandle.SetUseSyslog(pacmanConf.UseSyslog); err != nil {
return err
}
return alpmHandle.SetCheckSpace(pacmanConf.CheckSpace)
}
func logCallback(level alpm.LogLevel, str string) {
switch level {
case alpm.LogWarning:
text.Warn(str)
case alpm.LogError:
text.Error(str)
}
}
2020-08-16 21:41:38 +00:00
func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
return func(question alpm.QuestionAny) {
if qi, err := question.QuestionInstallIgnorepkg(); err == nil {
qi.SetInstall(true)
}
qp, err := question.QuestionSelectProvider()
if err != nil {
return
}
if settings.HideMenus {
return
}
size := 0
2020-09-06 19:13:05 +00:00
_ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
2020-08-16 21:41:38 +00:00
size++
return nil
})
str := text.Bold(gotext.Get("There are %d providers available for %s:\n", size, qp.Dep()))
size = 1
2021-08-11 18:13:28 +00:00
2020-08-16 21:41:38 +00:00
var dbName string
2020-09-06 19:13:05 +00:00
_ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
2020-08-16 21:41:38 +00:00
thisDB := pkg.DB().Name()
if dbName != thisDB {
dbName = thisDB
str += text.SprintOperationInfo(gotext.Get("Repository"), dbName, "\n ")
}
str += fmt.Sprintf("%d) %s ", size, pkg.Name())
size++
return nil
})
text.OperationInfoln(str)
for {
fmt.Print(gotext.Get("\nEnter a number (default=1): "))
// TODO: reenable noconfirm
if settings.NoConfirm {
fmt.Println()
2021-08-11 18:13:28 +00:00
break
}
2020-08-16 21:41:38 +00:00
numberBuf, err := text.GetInput("", false)
2020-08-16 21:41:38 +00:00
if err != nil {
text.Errorln(err)
break
}
if numberBuf == "" {
2020-08-16 21:41:38 +00:00
break
}
num, err := strconv.Atoi(numberBuf)
2020-08-16 21:41:38 +00:00
if err != nil {
text.Errorln(gotext.Get("invalid number: %s", numberBuf))
2020-08-16 21:41:38 +00:00
continue
}
if num < 1 || num > size {
text.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
continue
}
qp.SetUseIndex(num - 1)
2021-08-11 18:13:28 +00:00
2020-08-16 21:41:38 +00:00
break
}
}
}
func (ae *AlpmExecutor) RefreshHandle() error {
if ae.handle != nil {
if errRelease := ae.handle.Release(); errRelease != nil {
return errRelease
}
}
alpmHandle, err := alpm.Initialize(ae.conf.RootDir, ae.conf.DBPath)
if err != nil {
return errors.New(gotext.Get("unable to CreateHandle: %s", err))
}
if errConf := configureAlpm(ae.conf, alpmHandle); errConf != nil {
return errConf
}
2021-05-05 07:49:43 +00:00
alpmSetQuestionCallback(alpmHandle, ae.questionCallback())
alpmSetLogCallback(alpmHandle, logCallback)
ae.handle = alpmHandle
ae.syncDBsCache = nil
2021-08-11 18:13:28 +00:00
ae.syncDB, err = alpmHandle.SyncDBs()
if err != nil {
return err
}
ae.localDB, err = alpmHandle.LocalDB()
2021-08-11 18:13:28 +00:00
return err
2020-07-28 23:53:25 +00:00
}
func (ae *AlpmExecutor) LocalSatisfierExists(pkgName string) bool {
if _, err := ae.localDB.PkgCache().FindSatisfier(pkgName); err != nil {
2020-07-28 23:53:25 +00:00
return false
}
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
return true
}
2020-07-31 23:20:00 +00:00
func (ae *AlpmExecutor) SyncSatisfierExists(pkgName string) bool {
if _, err := ae.syncDB.FindSatisfier(pkgName); err != nil {
return false
}
2021-08-11 18:13:28 +00:00
2020-07-31 23:20:00 +00:00
return true
}
2020-07-28 23:53:25 +00:00
func (ae *AlpmExecutor) IsCorrectVersionInstalled(pkgName, versionRequired string) bool {
alpmPackage := ae.localDB.Pkg(pkgName)
2020-07-28 23:53:25 +00:00
if alpmPackage == nil {
return false
}
return alpmPackage.Version() == versionRequired
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) SyncSatisfier(pkgName string) alpm.IPackage {
foundPkg, err := ae.syncDB.FindSatisfier(pkgName)
2020-07-28 23:53:25 +00:00
if err != nil {
return nil
}
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
return foundPkg
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
groupPackages := []alpm.IPackage{}
2020-09-06 19:13:05 +00:00
_ = ae.syncDB.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
groupPackages = append(groupPackages, pkg)
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
return nil
})
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
return groupPackages
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
localPackages := []alpm.IPackage{}
2020-09-06 19:13:05 +00:00
_ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
2020-10-01 11:38:03 +00:00
localPackages = append(localPackages, pkg)
2020-07-28 23:53:25 +00:00
return nil
})
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
return localPackages
}
2021-08-11 18:13:28 +00:00
// SyncPackages searches SyncDB for packages or returns all packages if no search param is given.
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) SyncPackages(pkgNames ...string) []alpm.IPackage {
repoPackages := []alpm.IPackage{}
2020-09-06 19:13:05 +00:00
_ = ae.syncDB.ForEach(func(alpmDB alpm.IDB) error {
if len(pkgNames) == 0 {
2020-09-06 19:13:05 +00:00
_ = alpmDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
2020-10-01 11:38:03 +00:00
repoPackages = append(repoPackages, pkg)
return nil
})
} else {
2020-09-06 19:13:05 +00:00
_ = alpmDB.Search(pkgNames).ForEach(func(pkg alpm.IPackage) error {
2020-10-01 11:38:03 +00:00
repoPackages = append(repoPackages, pkg)
return nil
})
}
return nil
})
2021-08-11 18:13:28 +00:00
return repoPackages
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) LocalPackage(pkgName string) alpm.IPackage {
pkg := ae.localDB.Pkg(pkgName)
if pkg == nil {
return nil
}
2021-08-11 18:13:28 +00:00
return pkg
}
func (ae *AlpmExecutor) syncDBs() []alpm.IDB {
if ae.syncDBsCache == nil {
ae.syncDBsCache = ae.syncDB.Slice()
}
2021-08-11 18:13:28 +00:00
return ae.syncDBsCache
}
func (ae *AlpmExecutor) SyncPackage(pkgName string) alpm.IPackage {
for _, db := range ae.syncDBs() {
if dbPkg := db.Pkg(pkgName); dbPkg != nil {
return dbPkg
}
}
2021-08-11 18:13:28 +00:00
return nil
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) alpm.IPackage {
singleDB, err := ae.handle.SyncDBByName(dbName)
2020-07-28 23:53:25 +00:00
if err != nil {
return nil
}
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
foundPkg, err := singleDB.PkgCache().FindSatisfier(pkgName)
if err != nil {
return nil
}
2021-08-11 18:13:28 +00:00
2020-07-28 23:53:25 +00:00
return foundPkg
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {
2020-07-28 23:53:25 +00:00
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Depends().Slice()
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) PackageOptionalDepends(pkg alpm.IPackage) []alpm.Depend {
2020-08-07 16:55:19 +00:00
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.OptionalDepends().Slice()
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) PackageProvides(pkg alpm.IPackage) []alpm.Depend {
2020-07-28 23:53:25 +00:00
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Provides().Slice()
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) PackageConflicts(pkg alpm.IPackage) []alpm.Depend {
2020-07-28 23:53:25 +00:00
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Conflicts().Slice()
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
alpmPackage := pkg.(*alpm.Package)
return alpmPackage.Groups().Slice()
}
2020-07-31 23:20:00 +00:00
// upRepo gathers local packages and checks if they have new versions.
// Output: Upgrade type package list.
func (ae *AlpmExecutor) RepoUpgrades(enableDowngrade bool) ([]db.Upgrade, error) {
2021-08-11 18:13:28 +00:00
var errReturn error
slice := []db.Upgrade{}
2020-07-31 23:20:00 +00:00
2021-08-11 18:13:28 +00:00
localDB, errDB := ae.handle.LocalDB()
if errDB != nil {
return slice, errDB
2020-07-31 23:20:00 +00:00
}
2021-08-11 18:13:28 +00:00
if err := ae.handle.TransInit(alpm.TransFlagNoLock); err != nil {
2020-07-31 23:20:00 +00:00
return slice, err
}
defer func() {
2021-08-11 18:13:28 +00:00
errReturn = ae.handle.TransRelease()
2020-07-31 23:20:00 +00:00
}()
2021-08-11 18:13:28 +00:00
if err := ae.handle.SyncSysupgrade(enableDowngrade); err != nil {
2020-07-31 23:20:00 +00:00
return slice, err
}
2021-08-11 18:13:28 +00:00
2020-09-06 19:13:05 +00:00
_ = ae.handle.TransGetAdd().ForEach(func(pkg alpm.IPackage) error {
2020-07-31 23:20:00 +00:00
localVer := "-"
reason := alpm.PkgReasonExplicit
2020-07-31 23:20:00 +00:00
if localPkg := localDB.Pkg(pkg.Name()); localPkg != nil {
localVer = localPkg.Version()
reason = localPkg.Reason()
2020-07-31 23:20:00 +00:00
}
slice = append(slice, upgrade.Upgrade{
Name: pkg.Name(),
Repository: pkg.DB().Name(),
LocalVersion: localVer,
RemoteVersion: pkg.Version(),
Reason: reason,
2020-07-31 23:20:00 +00:00
})
return nil
})
2021-08-11 18:13:28 +00:00
return slice, errReturn
2020-07-31 23:20:00 +00:00
}
2020-10-01 11:38:03 +00:00
func (ae *AlpmExecutor) BiggestPackages() []alpm.IPackage {
localPackages := []alpm.IPackage{}
2020-09-06 19:13:05 +00:00
_ = ae.localDB.PkgCache().SortBySize().ForEach(func(pkg alpm.IPackage) error {
2020-10-01 11:38:03 +00:00
localPackages = append(localPackages, pkg)
2020-08-04 20:00:07 +00:00
return nil
})
2021-08-11 18:13:28 +00:00
2020-08-04 20:00:07 +00:00
return localPackages
}
2020-08-08 16:43:37 +00:00
func (ae *AlpmExecutor) LastBuildTime() time.Time {
var lastTime time.Time
2021-08-11 18:13:28 +00:00
2020-09-06 19:13:05 +00:00
_ = ae.syncDB.ForEach(func(db alpm.IDB) error {
_ = db.PkgCache().ForEach(func(pkg alpm.IPackage) error {
2020-08-08 16:43:37 +00:00
thisTime := pkg.BuildDate()
if thisTime.After(lastTime) {
lastTime = thisTime
}
return nil
})
return nil
})
return lastTime
}
func (ae *AlpmExecutor) Cleanup() {
if ae.handle != nil {
if err := ae.handle.Release(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
2021-04-19 11:43:13 +00:00
func (ae *AlpmExecutor) Repos() (repos []string) {
_ = ae.syncDB.ForEach(func(db alpm.IDB) error {
repos = append(repos, db.Name())
return nil
})
2021-08-11 18:13:28 +00:00
2021-04-19 11:43:13 +00:00
return
}
2021-07-03 16:12:17 +00:00
func alpmSetArchitecture(alpmHandle *alpm.Handle, arch []string) error {
return alpmHandle.SetArchitectures(arch)
}
func (ae *AlpmExecutor) AlpmArchitectures() ([]string, error) {
architectures, err := ae.handle.GetArchitectures()
return architectures.Slice(), err
}
func alpmSetLogCallback(alpmHandle *alpm.Handle, cb func(alpm.LogLevel, string)) {
alpmHandle.SetLogCallback(func(ctx interface{}, lvl alpm.LogLevel, msg string) {
cbo := ctx.(func(alpm.LogLevel, string))
cbo(lvl, msg)
}, cb)
}
func alpmSetQuestionCallback(alpmHandle *alpm.Handle, cb func(alpm.QuestionAny)) {
alpmHandle.SetQuestionCallback(func(ctx interface{}, q alpm.QuestionAny) {
cbo := ctx.(func(alpm.QuestionAny))
cbo(q)
}, cb)
}