alpm.go 11 KB

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