alpm.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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/settings"
  13. "github.com/Jguer/yay/v10/pkg/text"
  14. "github.com/Jguer/yay/v10/pkg/upgrade"
  15. )
  16. type AlpmExecutor struct {
  17. handle *alpm.Handle
  18. localDB alpm.IDB
  19. syncDB alpm.IDBList
  20. conf *pacmanconf.Config
  21. }
  22. func NewExecutor(pacmanConf *pacmanconf.Config) (*AlpmExecutor, error) {
  23. ae := &AlpmExecutor{conf: pacmanConf}
  24. err := ae.RefreshHandle()
  25. if err != nil {
  26. return nil, err
  27. }
  28. ae.localDB, err = ae.handle.LocalDB()
  29. if err != nil {
  30. return nil, err
  31. }
  32. ae.syncDB, err = ae.handle.SyncDBs()
  33. if err != nil {
  34. return nil, err
  35. }
  36. return ae, nil
  37. }
  38. func toUsage(usages []string) alpm.Usage {
  39. if len(usages) == 0 {
  40. return alpm.UsageAll
  41. }
  42. var ret alpm.Usage
  43. for _, usage := range usages {
  44. switch usage {
  45. case "Sync":
  46. ret |= alpm.UsageSync
  47. case "Search":
  48. ret |= alpm.UsageSearch
  49. case "Install":
  50. ret |= alpm.UsageInstall
  51. case "Upgrade":
  52. ret |= alpm.UsageUpgrade
  53. case "All":
  54. ret |= alpm.UsageAll
  55. }
  56. }
  57. return ret
  58. }
  59. func configureAlpm(pacmanConf *pacmanconf.Config, alpmHandle *alpm.Handle) error {
  60. // TODO: set SigLevel
  61. // sigLevel := alpm.SigPackage | alpm.SigPackageOptional | alpm.SigDatabase | alpm.SigDatabaseOptional
  62. // localFileSigLevel := alpm.SigUseDefault
  63. // remoteFileSigLevel := alpm.SigUseDefault
  64. for _, repo := range pacmanConf.Repos {
  65. // TODO: set SigLevel
  66. alpmDB, err := alpmHandle.RegisterSyncDB(repo.Name, 0)
  67. if err != nil {
  68. return err
  69. }
  70. alpmDB.SetServers(repo.Servers)
  71. alpmDB.SetUsage(toUsage(repo.Usage))
  72. }
  73. if err := alpmHandle.SetCacheDirs(pacmanConf.CacheDir); err != nil {
  74. return err
  75. }
  76. // add hook directories 1-by-1 to avoid overwriting the system directory
  77. for _, dir := range pacmanConf.HookDir {
  78. if err := alpmHandle.AddHookDir(dir); err != nil {
  79. return err
  80. }
  81. }
  82. if err := alpmHandle.SetGPGDir(pacmanConf.GPGDir); err != nil {
  83. return err
  84. }
  85. if err := alpmHandle.SetLogFile(pacmanConf.LogFile); err != nil {
  86. return err
  87. }
  88. if err := alpmHandle.SetIgnorePkgs(pacmanConf.IgnorePkg); err != nil {
  89. return err
  90. }
  91. if err := alpmHandle.SetIgnoreGroups(pacmanConf.IgnoreGroup); err != nil {
  92. return err
  93. }
  94. if err := alpmHandle.SetArch(pacmanConf.Architecture); err != nil {
  95. return err
  96. }
  97. if err := alpmHandle.SetNoUpgrades(pacmanConf.NoUpgrade); err != nil {
  98. return err
  99. }
  100. if err := alpmHandle.SetNoExtracts(pacmanConf.NoExtract); err != nil {
  101. return err
  102. }
  103. /*if err := alpmHandle.SetDefaultSigLevel(sigLevel); err != nil {
  104. return err
  105. }
  106. if err := alpmHandle.SetLocalFileSigLevel(localFileSigLevel); err != nil {
  107. return err
  108. }
  109. if err := alpmHandle.SetRemoteFileSigLevel(remoteFileSigLevel); 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:\n", 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 += 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.Print(gotext.Get("\nEnter a number (default=1): "))
  158. // TODO: reenable noconfirm
  159. if settings.NoConfirm {
  160. fmt.Println()
  161. break
  162. }
  163. reader := bufio.NewReader(os.Stdin)
  164. numberBuf, overflow, err := reader.ReadLine()
  165. if err != nil {
  166. text.Errorln(err)
  167. break
  168. }
  169. if overflow {
  170. text.Errorln(gotext.Get(" Input too long"))
  171. continue
  172. }
  173. if string(numberBuf) == "" {
  174. break
  175. }
  176. num, err := strconv.Atoi(string(numberBuf))
  177. if err != nil {
  178. text.Errorln(gotext.Get("invalid number: %s", string(numberBuf)))
  179. continue
  180. }
  181. if num < 1 || num > size {
  182. text.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
  183. continue
  184. }
  185. qp.SetUseIndex(num - 1)
  186. break
  187. }
  188. }
  189. }
  190. func (ae *AlpmExecutor) RefreshHandle() error {
  191. if ae.handle != nil {
  192. if errRelease := ae.handle.Release(); errRelease != nil {
  193. return errRelease
  194. }
  195. }
  196. alpmHandle, err := alpm.Initialize(ae.conf.RootDir, ae.conf.DBPath)
  197. if err != nil {
  198. return errors.New(gotext.Get("unable to CreateHandle: %s", err))
  199. }
  200. if errConf := configureAlpm(ae.conf, alpmHandle); errConf != nil {
  201. return errConf
  202. }
  203. alpmHandle.SetQuestionCallback(ae.questionCallback())
  204. alpmHandle.SetLogCallback(logCallback)
  205. ae.handle = alpmHandle
  206. ae.syncDB, err = alpmHandle.SyncDBs()
  207. if err != nil {
  208. return err
  209. }
  210. ae.localDB, err = alpmHandle.LocalDB()
  211. return err
  212. }
  213. func (ae *AlpmExecutor) LocalSatisfierExists(pkgName string) bool {
  214. if _, err := ae.localDB.PkgCache().FindSatisfier(pkgName); err != nil {
  215. return false
  216. }
  217. return true
  218. }
  219. func (ae *AlpmExecutor) SyncSatisfierExists(pkgName string) bool {
  220. if _, err := ae.syncDB.FindSatisfier(pkgName); err != nil {
  221. return false
  222. }
  223. return true
  224. }
  225. func (ae *AlpmExecutor) IsCorrectVersionInstalled(pkgName, versionRequired string) bool {
  226. alpmPackage := ae.localDB.Pkg(pkgName)
  227. if alpmPackage == nil {
  228. return false
  229. }
  230. return alpmPackage.Version() == versionRequired
  231. }
  232. func (ae *AlpmExecutor) SyncSatisfier(pkgName string) alpm.IPackage {
  233. foundPkg, err := ae.syncDB.FindSatisfier(pkgName)
  234. if err != nil {
  235. return nil
  236. }
  237. return foundPkg
  238. }
  239. func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
  240. groupPackages := []alpm.IPackage{}
  241. _ = ae.syncDB.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
  242. groupPackages = append(groupPackages, pkg)
  243. return nil
  244. })
  245. return groupPackages
  246. }
  247. func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
  248. localPackages := []alpm.IPackage{}
  249. _ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
  250. localPackages = append(localPackages, pkg)
  251. return nil
  252. })
  253. return localPackages
  254. }
  255. // SyncPackages searches SyncDB for packages or returns all packages if no search param is given
  256. func (ae *AlpmExecutor) SyncPackages(pkgNames ...string) []alpm.IPackage {
  257. repoPackages := []alpm.IPackage{}
  258. _ = ae.syncDB.ForEach(func(alpmDB alpm.IDB) error {
  259. if len(pkgNames) == 0 {
  260. _ = alpmDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
  261. repoPackages = append(repoPackages, pkg)
  262. return nil
  263. })
  264. } else {
  265. _ = alpmDB.Search(pkgNames).ForEach(func(pkg alpm.IPackage) error {
  266. repoPackages = append(repoPackages, pkg)
  267. return nil
  268. })
  269. }
  270. return nil
  271. })
  272. return repoPackages
  273. }
  274. func (ae *AlpmExecutor) LocalPackage(pkgName string) alpm.IPackage {
  275. pkg := ae.localDB.Pkg(pkgName)
  276. if pkg == nil {
  277. return nil
  278. }
  279. return pkg
  280. }
  281. func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) alpm.IPackage {
  282. singleDB, err := ae.handle.SyncDBByName(dbName)
  283. if err != nil {
  284. return nil
  285. }
  286. foundPkg, err := singleDB.PkgCache().FindSatisfier(pkgName)
  287. if err != nil {
  288. return nil
  289. }
  290. return foundPkg
  291. }
  292. func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {
  293. alpmPackage := pkg.(*alpm.Package)
  294. return alpmPackage.Depends().Slice()
  295. }
  296. func (ae *AlpmExecutor) PackageOptionalDepends(pkg alpm.IPackage) []alpm.Depend {
  297. alpmPackage := pkg.(*alpm.Package)
  298. return alpmPackage.OptionalDepends().Slice()
  299. }
  300. func (ae *AlpmExecutor) PackageProvides(pkg alpm.IPackage) []alpm.Depend {
  301. alpmPackage := pkg.(*alpm.Package)
  302. return alpmPackage.Provides().Slice()
  303. }
  304. func (ae *AlpmExecutor) PackageConflicts(pkg alpm.IPackage) []alpm.Depend {
  305. alpmPackage := pkg.(*alpm.Package)
  306. return alpmPackage.Conflicts().Slice()
  307. }
  308. func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
  309. alpmPackage := pkg.(*alpm.Package)
  310. return alpmPackage.Groups().Slice()
  311. }
  312. // upRepo gathers local packages and checks if they have new versions.
  313. // Output: Upgrade type package list.
  314. func (ae *AlpmExecutor) RepoUpgrades(enableDowngrade bool) (upgrade.UpSlice, error) {
  315. slice := upgrade.UpSlice{}
  316. localDB, err := ae.handle.LocalDB()
  317. if err != nil {
  318. return slice, err
  319. }
  320. err = ae.handle.TransInit(alpm.TransFlagNoLock)
  321. if err != nil {
  322. return slice, err
  323. }
  324. defer func() {
  325. err = ae.handle.TransRelease()
  326. }()
  327. err = ae.handle.SyncSysupgrade(enableDowngrade)
  328. if err != nil {
  329. return slice, err
  330. }
  331. _ = ae.handle.TransGetAdd().ForEach(func(pkg alpm.IPackage) error {
  332. localVer := "-"
  333. if localPkg := localDB.Pkg(pkg.Name()); localPkg != nil {
  334. localVer = localPkg.Version()
  335. }
  336. slice = append(slice, upgrade.Upgrade{
  337. Name: pkg.Name(),
  338. Repository: pkg.DB().Name(),
  339. LocalVersion: localVer,
  340. RemoteVersion: pkg.Version(),
  341. })
  342. return nil
  343. })
  344. return slice, nil
  345. }
  346. func (ae *AlpmExecutor) AlpmArch() (string, error) {
  347. return ae.handle.Arch()
  348. }
  349. func (ae *AlpmExecutor) BiggestPackages() []alpm.IPackage {
  350. localPackages := []alpm.IPackage{}
  351. _ = ae.localDB.PkgCache().SortBySize().ForEach(func(pkg alpm.IPackage) error {
  352. localPackages = append(localPackages, pkg)
  353. return nil
  354. })
  355. return localPackages
  356. }
  357. func (ae *AlpmExecutor) LastBuildTime() time.Time {
  358. var lastTime time.Time
  359. _ = ae.syncDB.ForEach(func(db alpm.IDB) error {
  360. _ = db.PkgCache().ForEach(func(pkg alpm.IPackage) error {
  361. thisTime := pkg.BuildDate()
  362. if thisTime.After(lastTime) {
  363. lastTime = thisTime
  364. }
  365. return nil
  366. })
  367. return nil
  368. })
  369. return lastTime
  370. }
  371. func (ae *AlpmExecutor) Cleanup() {
  372. if ae.handle != nil {
  373. if err := ae.handle.Release(); err != nil {
  374. fmt.Fprintln(os.Stderr, err)
  375. }
  376. }
  377. }