alpm.go 11 KB

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