alpm.go 11 KB

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