alpm.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. package ialpm
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "strconv"
  7. "time"
  8. alpm "github.com/Jguer/go-alpm/v2"
  9. pacmanconf "github.com/Morganamilo/go-pacmanconf"
  10. "github.com/leonelquinteros/gotext"
  11. "github.com/Jguer/yay/v12/pkg/db"
  12. "github.com/Jguer/yay/v12/pkg/settings"
  13. "github.com/Jguer/yay/v12/pkg/text"
  14. )
  15. type AlpmExecutor struct {
  16. handle *alpm.Handle
  17. localDB alpm.IDB
  18. syncDB alpm.IDBList
  19. syncDBsCache []alpm.IDB
  20. conf *pacmanconf.Config
  21. log *text.Logger
  22. installedRemotePkgNames []string
  23. installedRemotePkgMap map[string]alpm.IPackage
  24. installedSyncPkgNames []string
  25. }
  26. func NewExecutor(pacmanConf *pacmanconf.Config, logger *text.Logger) (*AlpmExecutor, error) {
  27. ae := &AlpmExecutor{
  28. handle: nil,
  29. localDB: nil,
  30. syncDB: nil,
  31. syncDBsCache: []alpm.IDB{},
  32. conf: pacmanConf,
  33. log: logger,
  34. installedRemotePkgNames: nil,
  35. installedRemotePkgMap: nil,
  36. installedSyncPkgNames: nil,
  37. }
  38. if err := ae.RefreshHandle(); err != nil {
  39. return nil, err
  40. }
  41. var err error
  42. ae.localDB, err = ae.handle.LocalDB()
  43. if err != nil {
  44. return nil, err
  45. }
  46. ae.syncDB, err = ae.handle.SyncDBs()
  47. if err != nil {
  48. return nil, err
  49. }
  50. return ae, nil
  51. }
  52. func toUsage(usages []string) alpm.Usage {
  53. if len(usages) == 0 {
  54. return alpm.UsageAll
  55. }
  56. var ret alpm.Usage
  57. for _, usage := range usages {
  58. switch usage {
  59. case "Sync":
  60. ret |= alpm.UsageSync
  61. case "Search":
  62. ret |= alpm.UsageSearch
  63. case "Install":
  64. ret |= alpm.UsageInstall
  65. case "Upgrade":
  66. ret |= alpm.UsageUpgrade
  67. case "All":
  68. ret |= alpm.UsageAll
  69. }
  70. }
  71. return ret
  72. }
  73. func configureAlpm(pacmanConf *pacmanconf.Config, alpmHandle *alpm.Handle) error {
  74. for _, repo := range pacmanConf.Repos {
  75. // TODO: set SigLevel
  76. alpmDB, err := alpmHandle.RegisterSyncDB(repo.Name, 0)
  77. if err != nil {
  78. return err
  79. }
  80. alpmDB.SetServers(repo.Servers)
  81. alpmDB.SetUsage(toUsage(repo.Usage))
  82. }
  83. if err := alpmHandle.SetCacheDirs(pacmanConf.CacheDir); err != nil {
  84. return err
  85. }
  86. // add hook directories 1-by-1 to avoid overwriting the system directory
  87. for _, dir := range pacmanConf.HookDir {
  88. if err := alpmHandle.AddHookDir(dir); err != nil {
  89. return err
  90. }
  91. }
  92. if err := alpmHandle.SetGPGDir(pacmanConf.GPGDir); err != nil {
  93. return err
  94. }
  95. if err := alpmHandle.SetLogFile(pacmanConf.LogFile); err != nil {
  96. return err
  97. }
  98. if err := alpmHandle.SetIgnorePkgs(pacmanConf.IgnorePkg); err != nil {
  99. return err
  100. }
  101. if err := alpmHandle.SetIgnoreGroups(pacmanConf.IgnoreGroup); err != nil {
  102. return err
  103. }
  104. if err := alpmSetArchitecture(alpmHandle, pacmanConf.Architecture); err != nil {
  105. return err
  106. }
  107. if err := alpmHandle.SetNoUpgrades(pacmanConf.NoUpgrade); err != nil {
  108. return err
  109. }
  110. if err := alpmHandle.SetNoExtracts(pacmanConf.NoExtract); err != nil {
  111. return err
  112. }
  113. if err := alpmHandle.SetUseSyslog(pacmanConf.UseSyslog); err != nil {
  114. return err
  115. }
  116. return alpmHandle.SetCheckSpace(pacmanConf.CheckSpace)
  117. }
  118. func (ae *AlpmExecutor) logCallback() func(level alpm.LogLevel, str string) {
  119. return func(level alpm.LogLevel, str string) {
  120. switch level {
  121. case alpm.LogWarning:
  122. ae.log.Warn(str)
  123. case alpm.LogError:
  124. ae.log.Error(str)
  125. }
  126. }
  127. }
  128. func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
  129. return func(question alpm.QuestionAny) {
  130. if qi, err := question.QuestionInstallIgnorepkg(); err == nil {
  131. qi.SetInstall(true)
  132. }
  133. qp, err := question.QuestionSelectProvider()
  134. if err != nil {
  135. return
  136. }
  137. if settings.HideMenus {
  138. return
  139. }
  140. size := 0
  141. _ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
  142. size++
  143. return nil
  144. })
  145. str := text.Bold(gotext.Get("There are %d providers available for %s:", size, qp.Dep()))
  146. size = 1
  147. var dbName string
  148. _ = qp.Providers(ae.handle).ForEach(func(pkg alpm.IPackage) error {
  149. thisDB := pkg.DB().Name()
  150. if dbName != thisDB {
  151. dbName = thisDB
  152. str += "\n"
  153. str += ae.log.SprintOperationInfo(gotext.Get("Repository"), " ", dbName, "\n ")
  154. }
  155. str += fmt.Sprintf("%d) %s ", size, pkg.Name())
  156. size++
  157. return nil
  158. })
  159. ae.log.OperationInfoln(str)
  160. for {
  161. ae.log.Println(gotext.Get("\nEnter a number (default=1): "))
  162. // TODO: reenable noconfirm
  163. if settings.NoConfirm {
  164. ae.log.Println()
  165. break
  166. }
  167. numberBuf, err := ae.log.GetInput("", false)
  168. if err != nil {
  169. ae.log.Errorln(err)
  170. break
  171. }
  172. if numberBuf == "" {
  173. break
  174. }
  175. num, err := strconv.Atoi(numberBuf)
  176. if err != nil {
  177. ae.log.Errorln(gotext.Get("invalid number: %s", numberBuf))
  178. continue
  179. }
  180. if num < 1 || num > size {
  181. ae.log.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
  182. continue
  183. }
  184. qp.SetUseIndex(num - 1)
  185. break
  186. }
  187. }
  188. }
  189. func (ae *AlpmExecutor) RefreshHandle() error {
  190. if ae.handle != nil {
  191. if errRelease := ae.handle.Release(); errRelease != nil {
  192. return errRelease
  193. }
  194. }
  195. alpmHandle, err := alpm.Initialize(ae.conf.RootDir, ae.conf.DBPath)
  196. if err != nil {
  197. return errors.New(gotext.Get("unable to CreateHandle: %s", err))
  198. }
  199. if errConf := configureAlpm(ae.conf, alpmHandle); errConf != nil {
  200. return errConf
  201. }
  202. alpmSetQuestionCallback(alpmHandle, ae.questionCallback())
  203. alpmSetLogCallback(alpmHandle, ae.logCallback())
  204. ae.handle = alpmHandle
  205. ae.syncDBsCache = nil
  206. ae.syncDB, err = alpmHandle.SyncDBs()
  207. if err != nil {
  208. return err
  209. }
  210. ae.localDB, err = alpmHandle.LocalDB()
  211. return err
  212. }
  213. func (ae *AlpmExecutor) LocalSatisfierExists(pkgName string) bool {
  214. if _, err := ae.localDB.PkgCache().FindSatisfier(pkgName); err != nil {
  215. return false
  216. }
  217. return true
  218. }
  219. func (ae *AlpmExecutor) SyncSatisfierExists(pkgName string) bool {
  220. if _, err := ae.syncDB.FindSatisfier(pkgName); err != nil {
  221. return false
  222. }
  223. return true
  224. }
  225. func (ae *AlpmExecutor) IsCorrectVersionInstalled(pkgName, versionRequired string) bool {
  226. alpmPackage := ae.localDB.Pkg(pkgName)
  227. if alpmPackage == nil {
  228. return false
  229. }
  230. return alpmPackage.Version() == versionRequired
  231. }
  232. func (ae *AlpmExecutor) SyncSatisfier(pkgName string) alpm.IPackage {
  233. foundPkg, err := ae.syncDB.FindSatisfier(pkgName)
  234. if err != nil {
  235. return nil
  236. }
  237. return foundPkg
  238. }
  239. func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
  240. groupPackages := []alpm.IPackage{}
  241. _ = ae.syncDB.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
  242. groupPackages = append(groupPackages, pkg)
  243. return nil
  244. })
  245. return groupPackages
  246. }
  247. func (ae *AlpmExecutor) PackagesFromGroupAndDB(groupName, dbName string) ([]alpm.IPackage, error) {
  248. singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
  249. if err != nil {
  250. return nil, err
  251. }
  252. groupPackages := []alpm.IPackage{}
  253. _ = singleDBList.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
  254. groupPackages = append(groupPackages, pkg)
  255. return nil
  256. })
  257. return groupPackages, nil
  258. }
  259. func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
  260. localPackages := []alpm.IPackage{}
  261. _ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
  262. localPackages = append(localPackages, pkg)
  263. return nil
  264. })
  265. return localPackages
  266. }
  267. // SyncPackages searches SyncDB for packages or returns all packages if no search param is given.
  268. func (ae *AlpmExecutor) SyncPackages(pkgNames ...string) []alpm.IPackage {
  269. repoPackages := []alpm.IPackage{}
  270. _ = ae.syncDB.ForEach(func(alpmDB alpm.IDB) error {
  271. if len(pkgNames) == 0 {
  272. _ = alpmDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
  273. repoPackages = append(repoPackages, pkg)
  274. return nil
  275. })
  276. } else {
  277. _ = alpmDB.Search(pkgNames).ForEach(func(pkg alpm.IPackage) error {
  278. repoPackages = append(repoPackages, pkg)
  279. return nil
  280. })
  281. }
  282. return nil
  283. })
  284. return repoPackages
  285. }
  286. func (ae *AlpmExecutor) LocalPackage(pkgName string) alpm.IPackage {
  287. pkg := ae.localDB.Pkg(pkgName)
  288. if pkg == nil {
  289. return nil
  290. }
  291. return pkg
  292. }
  293. func (ae *AlpmExecutor) syncDBs() []alpm.IDB {
  294. if ae.syncDBsCache == nil {
  295. ae.syncDBsCache = ae.syncDB.Slice()
  296. }
  297. return ae.syncDBsCache
  298. }
  299. func (ae *AlpmExecutor) SyncPackage(pkgName string) alpm.IPackage {
  300. for _, db := range ae.syncDBs() {
  301. if dbPkg := db.Pkg(pkgName); dbPkg != nil {
  302. return dbPkg
  303. }
  304. }
  305. return nil
  306. }
  307. func (ae *AlpmExecutor) SyncPackageFromDB(pkgName, dbName string) alpm.IPackage {
  308. singleDB, err := ae.handle.SyncDBByName(dbName)
  309. if err != nil {
  310. return nil
  311. }
  312. return singleDB.Pkg(pkgName)
  313. }
  314. func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) (alpm.IPackage, error) {
  315. singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
  316. if err != nil {
  317. return nil, err
  318. }
  319. foundPkg, err := singleDBList.FindSatisfier(pkgName)
  320. if err != nil {
  321. return nil, nil
  322. }
  323. return foundPkg, nil
  324. }
  325. func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {
  326. alpmPackage := pkg.(*alpm.Package)
  327. return alpmPackage.Depends().Slice()
  328. }
  329. func (ae *AlpmExecutor) PackageOptionalDepends(pkg alpm.IPackage) []alpm.Depend {
  330. alpmPackage := pkg.(*alpm.Package)
  331. return alpmPackage.OptionalDepends().Slice()
  332. }
  333. func (ae *AlpmExecutor) PackageProvides(pkg alpm.IPackage) []alpm.Depend {
  334. alpmPackage := pkg.(*alpm.Package)
  335. return alpmPackage.Provides().Slice()
  336. }
  337. func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
  338. alpmPackage := pkg.(*alpm.Package)
  339. return alpmPackage.Groups().Slice()
  340. }
  341. // upRepo gathers local packages and checks if they have new versions.
  342. // Output: Upgrade type package list.
  343. func (ae *AlpmExecutor) SyncUpgrades(enableDowngrade bool) (
  344. map[string]db.SyncUpgrade, error,
  345. ) {
  346. ups := map[string]db.SyncUpgrade{}
  347. var errReturn error
  348. localDB, errDB := ae.handle.LocalDB()
  349. if errDB != nil {
  350. return ups, errDB
  351. }
  352. if err := ae.handle.TransInit(alpm.TransFlagNoLock); err != nil {
  353. return ups, err
  354. }
  355. defer func() {
  356. errReturn = ae.handle.TransRelease()
  357. }()
  358. if err := ae.handle.SyncSysupgrade(enableDowngrade); err != nil {
  359. return ups, err
  360. }
  361. _ = ae.handle.TransGetAdd().ForEach(func(pkg alpm.IPackage) error {
  362. localVer := "-"
  363. reason := alpm.PkgReasonExplicit
  364. if localPkg := localDB.Pkg(pkg.Name()); localPkg != nil {
  365. localVer = localPkg.Version()
  366. reason = localPkg.Reason()
  367. }
  368. ups[pkg.Name()] = db.SyncUpgrade{
  369. Package: pkg,
  370. Reason: reason,
  371. LocalVersion: localVer,
  372. }
  373. return nil
  374. })
  375. return ups, errReturn
  376. }
  377. func (ae *AlpmExecutor) BiggestPackages() []alpm.IPackage {
  378. localPackages := []alpm.IPackage{}
  379. _ = ae.localDB.PkgCache().SortBySize().ForEach(func(pkg alpm.IPackage) error {
  380. localPackages = append(localPackages, pkg)
  381. return nil
  382. })
  383. return localPackages
  384. }
  385. func (ae *AlpmExecutor) LastBuildTime() time.Time {
  386. var lastTime time.Time
  387. _ = ae.syncDB.ForEach(func(db alpm.IDB) error {
  388. _ = db.PkgCache().ForEach(func(pkg alpm.IPackage) error {
  389. thisTime := pkg.BuildDate()
  390. if thisTime.After(lastTime) {
  391. lastTime = thisTime
  392. }
  393. return nil
  394. })
  395. return nil
  396. })
  397. return lastTime
  398. }
  399. func (ae *AlpmExecutor) Cleanup() {
  400. if ae.handle != nil {
  401. if err := ae.handle.Release(); err != nil {
  402. fmt.Fprintln(os.Stderr, err)
  403. }
  404. }
  405. }
  406. func (ae *AlpmExecutor) Repos() (repos []string) {
  407. _ = ae.syncDB.ForEach(func(db alpm.IDB) error {
  408. repos = append(repos, db.Name())
  409. return nil
  410. })
  411. return
  412. }
  413. func alpmSetArchitecture(alpmHandle *alpm.Handle, arch []string) error {
  414. return alpmHandle.SetArchitectures(arch)
  415. }
  416. func (ae *AlpmExecutor) AlpmArchitectures() ([]string, error) {
  417. architectures, err := ae.handle.GetArchitectures()
  418. return architectures.Slice(), err
  419. }
  420. func alpmSetLogCallback(alpmHandle *alpm.Handle, cb func(alpm.LogLevel, string)) {
  421. alpmHandle.SetLogCallback(func(ctx interface{}, lvl alpm.LogLevel, msg string) {
  422. cbo := ctx.(func(alpm.LogLevel, string))
  423. cbo(lvl, msg)
  424. }, cb)
  425. }
  426. func alpmSetQuestionCallback(alpmHandle *alpm.Handle, cb func(alpm.QuestionAny)) {
  427. alpmHandle.SetQuestionCallback(func(ctx interface{}, q alpm.QuestionAny) {
  428. cbo := ctx.(func(alpm.QuestionAny))
  429. cbo(q)
  430. }, cb)
  431. }