rlimit.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. //go:build !windows && !plan9 && !js
  2. package unix
  3. import (
  4. "fmt"
  5. "sync"
  6. "golang.org/x/sys/unix"
  7. "src.elv.sh/pkg/eval/errs"
  8. "src.elv.sh/pkg/eval/vals"
  9. )
  10. type rlimitsVar struct{}
  11. var (
  12. getRlimit = unix.Getrlimit
  13. setRlimit = unix.Setrlimit
  14. )
  15. var (
  16. rlimitMutex sync.Mutex
  17. rlimits map[int]*unix.Rlimit
  18. )
  19. func (rlimitsVar) Get() any {
  20. rlimitMutex.Lock()
  21. defer rlimitMutex.Unlock()
  22. initRlimits()
  23. rlimitsMap := vals.EmptyMap
  24. for res, lim := range rlimits {
  25. limMap := vals.EmptyMap
  26. if lim.Cur != unix.RLIM_INFINITY {
  27. limMap = limMap.Assoc("cur", convertRlimT(lim.Cur))
  28. }
  29. if lim.Max != unix.RLIM_INFINITY {
  30. limMap = limMap.Assoc("max", convertRlimT(lim.Max))
  31. }
  32. rlimitsMap = rlimitsMap.Assoc(rlimitKeys[res], limMap)
  33. }
  34. return rlimitsMap
  35. }
  36. func (rlimitsVar) Set(v any) error {
  37. newRlimits, err := parseRlimitsMap(v)
  38. if err != nil {
  39. return err
  40. }
  41. rlimitMutex.Lock()
  42. defer rlimitMutex.Unlock()
  43. initRlimits()
  44. for res := range rlimits {
  45. if *rlimits[res] != *newRlimits[res] {
  46. err := setRlimit(res, newRlimits[res])
  47. if err != nil {
  48. return fmt.Errorf("setrlimit %s: %w", rlimitKeys[res], err)
  49. }
  50. rlimits[res] = newRlimits[res]
  51. }
  52. }
  53. return nil
  54. }
  55. func initRlimits() {
  56. if rlimits != nil {
  57. return
  58. }
  59. rlimits = make(map[int]*unix.Rlimit)
  60. for res := range rlimitKeys {
  61. var lim unix.Rlimit
  62. err := getRlimit(res, &lim)
  63. if err == nil {
  64. rlimits[res] = &lim
  65. } else {
  66. // Since getrlimit should only ever return an error when the
  67. // resource is not supported, this should normally never happen. But
  68. // be defensive nonetheless.
  69. logger.Println("initialize rlimits", res, rlimitKeys[res], err)
  70. // Remove this key, so that rlimitKeys is always consistent with the
  71. // value of rlimits (and thus $unix:rlimits).
  72. delete(rlimitKeys, res)
  73. }
  74. }
  75. }
  76. func parseRlimitsMap(val any) (map[int]*unix.Rlimit, error) {
  77. if err := checkRlimitsMapKeys(val); err != nil {
  78. return nil, err
  79. }
  80. limits := make(map[int]*unix.Rlimit, len(rlimitKeys))
  81. for res, key := range rlimitKeys {
  82. limitVal, err := vals.Index(val, key)
  83. if err != nil {
  84. return nil, err
  85. }
  86. limits[res], err = parseRlimitMap(limitVal)
  87. if err != nil {
  88. return nil, err
  89. }
  90. }
  91. return limits, nil
  92. }
  93. func checkRlimitsMapKeys(val any) error {
  94. wantedKeys := make(map[string]struct{}, len(rlimitKeys))
  95. for _, key := range rlimitKeys {
  96. wantedKeys[key] = struct{}{}
  97. }
  98. var errKey error
  99. err := vals.IterateKeys(val, func(k any) bool {
  100. ks, ok := k.(string)
  101. if !ok {
  102. errKey = errs.BadValue{What: "key of $unix:rlimits",
  103. Valid: "string", Actual: vals.Kind(k)}
  104. return false
  105. }
  106. if _, valid := wantedKeys[ks]; !valid {
  107. errKey = errs.BadValue{What: "key of $unix:rlimits",
  108. Valid: "valid resource key", Actual: vals.ReprPlain(k)}
  109. return false
  110. }
  111. delete(wantedKeys, ks)
  112. return true
  113. })
  114. if err != nil {
  115. return errs.BadValue{What: "$unix:rlimits",
  116. Valid: "map", Actual: vals.Kind(val)}
  117. }
  118. if errKey != nil {
  119. return errKey
  120. }
  121. if len(wantedKeys) > 0 {
  122. return errs.BadValue{What: "$unix:rlimits",
  123. Valid: "map containing all resource keys", Actual: vals.ReprPlain(val)}
  124. }
  125. return nil
  126. }
  127. func parseRlimitMap(val any) (*unix.Rlimit, error) {
  128. if err := checkRlimitMapKeys(val); err != nil {
  129. return nil, err
  130. }
  131. cur, err := indexRlimitMap(val, "cur")
  132. if err != nil {
  133. return nil, err
  134. }
  135. max, err := indexRlimitMap(val, "max")
  136. if err != nil {
  137. return nil, err
  138. }
  139. return &unix.Rlimit{Cur: cur, Max: max}, nil
  140. }
  141. func checkRlimitMapKeys(val any) error {
  142. var errKey error
  143. err := vals.IterateKeys(val, func(k any) bool {
  144. if k != "cur" && k != "max" {
  145. errKey = errs.BadValue{What: "key of rlimit value",
  146. Valid: "cur or max", Actual: vals.ReprPlain(k)}
  147. return false
  148. }
  149. return true
  150. })
  151. if err != nil {
  152. return errs.BadValue{What: "rlimit value",
  153. Valid: "map", Actual: vals.Kind(val)}
  154. }
  155. return errKey
  156. }
  157. func indexRlimitMap(m any, key string) (rlimT, error) {
  158. val, err := vals.Index(m, key)
  159. if err != nil {
  160. return unix.RLIM_INFINITY, nil
  161. }
  162. if r, ok := parseRlimT(val); ok {
  163. return r, nil
  164. }
  165. return 0, errs.BadValue{What: key + " in rlimit value",
  166. Valid: rlimTValid, Actual: vals.ReprPlain(val)}
  167. }