blob: 5ac7fd825fceea82aa5fa7303d5eda19db70ccba [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package units
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7)
8
9// Ulimit is a human friendly version of Rlimit.
10type Ulimit struct {
11 Name string
12 Hard int64
13 Soft int64
14}
15
16// Rlimit specifies the resource limits, such as max open files.
17type Rlimit struct {
18 Type int `json:"type,omitempty"`
19 Hard uint64 `json:"hard,omitempty"`
20 Soft uint64 `json:"soft,omitempty"`
21}
22
23const (
24 // magic numbers for making the syscall
25 // some of these are defined in the syscall package, but not all.
26 // Also since Windows client doesn't get access to the syscall package, need to
27 // define these here
28 rlimitAs = 9
29 rlimitCore = 4
30 rlimitCPU = 0
31 rlimitData = 2
32 rlimitFsize = 1
33 rlimitLocks = 10
34 rlimitMemlock = 8
35 rlimitMsgqueue = 12
36 rlimitNice = 13
37 rlimitNofile = 7
38 rlimitNproc = 6
39 rlimitRss = 5
40 rlimitRtprio = 14
41 rlimitRttime = 15
42 rlimitSigpending = 11
43 rlimitStack = 3
44)
45
46var ulimitNameMapping = map[string]int{
47 //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
48 "core": rlimitCore,
49 "cpu": rlimitCPU,
50 "data": rlimitData,
51 "fsize": rlimitFsize,
52 "locks": rlimitLocks,
53 "memlock": rlimitMemlock,
54 "msgqueue": rlimitMsgqueue,
55 "nice": rlimitNice,
56 "nofile": rlimitNofile,
57 "nproc": rlimitNproc,
58 "rss": rlimitRss,
59 "rtprio": rlimitRtprio,
60 "rttime": rlimitRttime,
61 "sigpending": rlimitSigpending,
62 "stack": rlimitStack,
63}
64
65// ParseUlimit parses and returns a Ulimit from the specified string.
66func ParseUlimit(val string) (*Ulimit, error) {
67 parts := strings.SplitN(val, "=", 2)
68 if len(parts) != 2 {
69 return nil, fmt.Errorf("invalid ulimit argument: %s", val)
70 }
71
72 if _, exists := ulimitNameMapping[parts[0]]; !exists {
73 return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
74 }
75
76 var (
77 soft int64
78 hard = &soft // default to soft in case no hard was set
79 temp int64
80 err error
81 )
82 switch limitVals := strings.Split(parts[1], ":"); len(limitVals) {
83 case 2:
84 temp, err = strconv.ParseInt(limitVals[1], 10, 64)
85 if err != nil {
86 return nil, err
87 }
88 hard = &temp
89 fallthrough
90 case 1:
91 soft, err = strconv.ParseInt(limitVals[0], 10, 64)
92 if err != nil {
93 return nil, err
94 }
95 default:
96 return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
97 }
98
99 if soft > *hard {
100 return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard)
101 }
102
103 return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil
104}
105
106// GetRlimit returns the RLimit corresponding to Ulimit.
107func (u *Ulimit) GetRlimit() (*Rlimit, error) {
108 t, exists := ulimitNameMapping[u.Name]
109 if !exists {
110 return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
111 }
112
113 return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
114}
115
116func (u *Ulimit) String() string {
117 return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard)
118}