alpm.go 11 KB

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