alpm.go 11 KB

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