123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- package ialpm
- import (
- "errors"
- "fmt"
- "os"
- "strconv"
- "time"
- alpm "github.com/Jguer/go-alpm/v2"
- pacmanconf "github.com/Morganamilo/go-pacmanconf"
- "github.com/leonelquinteros/gotext"
- "github.com/Jguer/yay/v12/pkg/db"
- "github.com/Jguer/yay/v12/pkg/settings"
- "github.com/Jguer/yay/v12/pkg/text"
- )
- type AlpmExecutor struct {
- handle *alpm.Handle
- localDB alpm.IDB
- syncDB alpm.IDBList
- syncDBsCache []alpm.IDB
- conf *pacmanconf.Config
- log *text.Logger
- installedRemotePkgNames []string
- installedRemotePkgMap map[string]alpm.IPackage
- installedSyncPkgNames []string
- }
- func NewExecutor(pacmanConf *pacmanconf.Config, logger *text.Logger) (*AlpmExecutor, error) {
- ae := &AlpmExecutor{
- handle: nil,
- localDB: nil,
- syncDB: nil,
- syncDBsCache: []alpm.IDB{},
- conf: pacmanConf,
- log: logger,
- installedRemotePkgNames: nil,
- installedRemotePkgMap: nil,
- installedSyncPkgNames: nil,
- }
- if err := ae.RefreshHandle(); err != nil {
- return nil, err
- }
- var err error
- ae.localDB, err = ae.handle.LocalDB()
- if err != nil {
- return nil, err
- }
- 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
- 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
- alpmDB, err := alpmHandle.RegisterSyncDB(repo.Name, 0)
- if err != nil {
- return err
- }
- 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 (ae *AlpmExecutor) logCallback() func(level alpm.LogLevel, str string) {
- return func(level alpm.LogLevel, str string) {
- switch level {
- case alpm.LogWarning:
- ae.log.Warn(str)
- case alpm.LogError:
- ae.log.Error(str)
- }
- }
- }
- 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
- _ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
- size++
- return nil
- })
- str := text.Bold(gotext.Get("There are %[1]d providers available for %[2]s:", size, qp.Dep()))
- size = 1
- var dbName string
- _ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
- thisDB := pkg.DB().Name()
- if dbName != thisDB {
- dbName = thisDB
- str += "\n"
- str += ae.log.SprintOperationInfo(gotext.Get("Repository"), " ", dbName, "\n ")
- }
- str += fmt.Sprintf("%d) %s ", size, pkg.Name())
- size++
- return nil
- })
- ae.log.OperationInfoln(str)
- for {
- ae.log.Println(gotext.Get("\nEnter a number (default=1): "))
- // TODO: reenable noconfirm
- if settings.NoConfirm {
- ae.log.Println()
- break
- }
- numberBuf, err := ae.log.GetInput("", false)
- if err != nil {
- ae.log.Errorln(err)
- break
- }
- if numberBuf == "" {
- break
- }
- num, err := strconv.Atoi(numberBuf)
- if err != nil {
- ae.log.Errorln(gotext.Get("invalid number: %s", numberBuf))
- continue
- }
- if num < 1 || num > size {
- ae.log.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
- continue
- }
- qp.SetUseIndex(num - 1)
- 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
- }
- alpmSetQuestionCallback(alpmHandle, ae.questionCallback())
- alpmSetLogCallback(alpmHandle, ae.logCallback())
- ae.handle = alpmHandle
- ae.syncDBsCache = nil
- ae.syncDB, err = alpmHandle.SyncDBs()
- if err != nil {
- return err
- }
- ae.localDB, err = alpmHandle.LocalDB()
- return err
- }
- func (ae *AlpmExecutor) LocalSatisfierExists(pkgName string) bool {
- if _, err := ae.localDB.PkgCache().FindSatisfier(pkgName); err != nil {
- return false
- }
- return true
- }
- func (ae *AlpmExecutor) SyncSatisfierExists(pkgName string) bool {
- if _, err := ae.syncDB.FindSatisfier(pkgName); err != nil {
- return false
- }
- return true
- }
- func (ae *AlpmExecutor) IsCorrectVersionInstalled(pkgName, versionRequired string) bool {
- alpmPackage := ae.localDB.Pkg(pkgName)
- if alpmPackage == nil {
- return false
- }
- return alpmPackage.Version() == versionRequired
- }
- func (ae *AlpmExecutor) SyncSatisfier(pkgName string) alpm.IPackage {
- foundPkg, err := ae.syncDB.FindSatisfier(pkgName)
- if err != nil {
- return nil
- }
- return foundPkg
- }
- func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
- groupPackages := []alpm.IPackage{}
- _ = ae.syncDB.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
- groupPackages = append(groupPackages, pkg)
- return nil
- })
- return groupPackages
- }
- func (ae *AlpmExecutor) PackagesFromGroupAndDB(groupName, dbName string) ([]alpm.IPackage, error) {
- singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
- if err != nil {
- return nil, err
- }
- groupPackages := []alpm.IPackage{}
- _ = singleDBList.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
- groupPackages = append(groupPackages, pkg)
- return nil
- })
- return groupPackages, nil
- }
- func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
- localPackages := []alpm.IPackage{}
- _ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
- localPackages = append(localPackages, pkg)
- return nil
- })
- return localPackages
- }
- // SyncPackages searches SyncDB for packages or returns all packages if no search param is given.
- func (ae *AlpmExecutor) SyncPackages(pkgNames ...string) []alpm.IPackage {
- repoPackages := []alpm.IPackage{}
- _ = ae.syncDB.ForEach(func(alpmDB alpm.IDB) error {
- if len(pkgNames) == 0 {
- _ = alpmDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
- repoPackages = append(repoPackages, pkg)
- return nil
- })
- } else {
- _ = alpmDB.Search(pkgNames).ForEach(func(pkg alpm.IPackage) error {
- repoPackages = append(repoPackages, pkg)
- return nil
- })
- }
- return nil
- })
- return repoPackages
- }
- func (ae *AlpmExecutor) LocalPackage(pkgName string) alpm.IPackage {
- pkg := ae.localDB.Pkg(pkgName)
- if pkg == nil {
- return nil
- }
- return pkg
- }
- func (ae *AlpmExecutor) syncDBs() []alpm.IDB {
- if ae.syncDBsCache == nil {
- ae.syncDBsCache = ae.syncDB.Slice()
- }
- 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
- }
- }
- return nil
- }
- func (ae *AlpmExecutor) SyncPackageFromDB(pkgName, dbName string) alpm.IPackage {
- singleDB, err := ae.handle.SyncDBByName(dbName)
- if err != nil {
- return nil
- }
- return singleDB.Pkg(pkgName)
- }
- func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) (alpm.IPackage, error) {
- singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
- if err != nil {
- return nil, err
- }
- foundPkg, err := singleDBList.FindSatisfier(pkgName)
- if err != nil {
- return nil, nil
- }
- return foundPkg, nil
- }
- func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {
- alpmPackage := pkg.(*alpm.Package)
- return alpmPackage.Depends().Slice()
- }
- func (ae *AlpmExecutor) PackageOptionalDepends(pkg alpm.IPackage) []alpm.Depend {
- alpmPackage := pkg.(*alpm.Package)
- return alpmPackage.OptionalDepends().Slice()
- }
- func (ae *AlpmExecutor) PackageProvides(pkg alpm.IPackage) []alpm.Depend {
- alpmPackage := pkg.(*alpm.Package)
- return alpmPackage.Provides().Slice()
- }
- func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
- alpmPackage := pkg.(*alpm.Package)
- return alpmPackage.Groups().Slice()
- }
- // upRepo gathers local packages and checks if they have new versions.
- // Output: Upgrade type package list.
- func (ae *AlpmExecutor) SyncUpgrades(enableDowngrade bool) (
- map[string]db.SyncUpgrade, error,
- ) {
- ups := map[string]db.SyncUpgrade{}
- var errReturn error
- localDB, errDB := ae.handle.LocalDB()
- if errDB != nil {
- return ups, errDB
- }
- if err := ae.handle.TransInit(alpm.TransFlagNoLock); err != nil {
- return ups, err
- }
- defer func() {
- errReturn = ae.handle.TransRelease()
- }()
- if err := ae.handle.SyncSysupgrade(enableDowngrade); err != nil {
- return ups, err
- }
- _ = ae.handle.TransGetAdd().ForEach(func(pkg alpm.IPackage) error {
- localVer := "-"
- reason := alpm.PkgReasonExplicit
- if localPkg := localDB.Pkg(pkg.Name()); localPkg != nil {
- localVer = localPkg.Version()
- reason = localPkg.Reason()
- }
- ups[pkg.Name()] = db.SyncUpgrade{
- Package: pkg,
- Reason: reason,
- LocalVersion: localVer,
- }
- return nil
- })
- return ups, errReturn
- }
- func (ae *AlpmExecutor) BiggestPackages() []alpm.IPackage {
- localPackages := []alpm.IPackage{}
- _ = ae.localDB.PkgCache().SortBySize().ForEach(func(pkg alpm.IPackage) error {
- localPackages = append(localPackages, pkg)
- return nil
- })
- return localPackages
- }
- func (ae *AlpmExecutor) LastBuildTime() time.Time {
- var lastTime time.Time
- _ = ae.syncDB.ForEach(func(db alpm.IDB) error {
- _ = db.PkgCache().ForEach(func(pkg alpm.IPackage) error {
- 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)
- }
- }
- }
- func (ae *AlpmExecutor) Repos() (repos []string) {
- _ = ae.syncDB.ForEach(func(db alpm.IDB) error {
- repos = append(repos, db.Name())
- return nil
- })
- return
- }
- 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)
- }
|