alpm.go 11 KB

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