1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package procfs
19
20 import (
21 "bufio"
22 "fmt"
23 "os"
24 "strconv"
25 "strings"
26
27 "golang.org/x/sys/unix"
28 )
29
30
31 type ProcMapPermissions struct {
32
33 Read bool
34
35 Write bool
36
37 Execute bool
38
39 Shared bool
40
41 Private bool
42 }
43
44
45
46 type ProcMap struct {
47
48 StartAddr uintptr
49
50 EndAddr uintptr
51
52 Perms *ProcMapPermissions
53
54 Offset int64
55
56 Dev uint64
57
58 Inode uint64
59
60 Pathname string
61 }
62
63
64
65 func parseDevice(s string) (uint64, error) {
66 i := strings.Index(s, ":")
67 if i == -1 {
68 return 0, fmt.Errorf("%w: expected separator `:` in %s", ErrFileParse, s)
69 }
70
71 major, err := strconv.ParseUint(s[0:i], 16, 0)
72 if err != nil {
73 return 0, err
74 }
75
76 minor, err := strconv.ParseUint(s[i+1:], 16, 0)
77 if err != nil {
78 return 0, err
79 }
80
81 return unix.Mkdev(uint32(major), uint32(minor)), nil
82 }
83
84
85 func parseAddress(s string) (uintptr, error) {
86 a, err := strconv.ParseUint(s, 16, 0)
87 if err != nil {
88 return 0, err
89 }
90
91 return uintptr(a), nil
92 }
93
94
95 func parseAddresses(s string) (uintptr, uintptr, error) {
96 idx := strings.Index(s, "-")
97 if idx == -1 {
98 return 0, 0, fmt.Errorf("%w: expected separator `-` in %s", ErrFileParse, s)
99 }
100
101 saddr, err := parseAddress(s[0:idx])
102 if err != nil {
103 return 0, 0, err
104 }
105
106 eaddr, err := parseAddress(s[idx+1:])
107 if err != nil {
108 return 0, 0, err
109 }
110
111 return saddr, eaddr, nil
112 }
113
114
115 func parsePermissions(s string) (*ProcMapPermissions, error) {
116 if len(s) < 4 {
117 return nil, fmt.Errorf("%w: invalid permissions token", ErrFileParse)
118 }
119
120 perms := ProcMapPermissions{}
121 for _, ch := range s {
122 switch ch {
123 case 'r':
124 perms.Read = true
125 case 'w':
126 perms.Write = true
127 case 'x':
128 perms.Execute = true
129 case 'p':
130 perms.Private = true
131 case 's':
132 perms.Shared = true
133 }
134 }
135
136 return &perms, nil
137 }
138
139
140
141 func parseProcMap(text string) (*ProcMap, error) {
142 fields := strings.Fields(text)
143 if len(fields) < 5 {
144 return nil, fmt.Errorf("%w: truncated procmap entry", ErrFileParse)
145 }
146
147 saddr, eaddr, err := parseAddresses(fields[0])
148 if err != nil {
149 return nil, err
150 }
151
152 perms, err := parsePermissions(fields[1])
153 if err != nil {
154 return nil, err
155 }
156
157 offset, err := strconv.ParseInt(fields[2], 16, 0)
158 if err != nil {
159 return nil, err
160 }
161
162 device, err := parseDevice(fields[3])
163 if err != nil {
164 return nil, err
165 }
166
167 inode, err := strconv.ParseUint(fields[4], 10, 0)
168 if err != nil {
169 return nil, err
170 }
171
172 pathname := ""
173
174 if len(fields) >= 5 {
175 pathname = strings.Join(fields[5:], " ")
176 }
177
178 return &ProcMap{
179 StartAddr: saddr,
180 EndAddr: eaddr,
181 Perms: perms,
182 Offset: offset,
183 Dev: device,
184 Inode: inode,
185 Pathname: pathname,
186 }, nil
187 }
188
189
190
191 func (p Proc) ProcMaps() ([]*ProcMap, error) {
192 file, err := os.Open(p.path("maps"))
193 if err != nil {
194 return nil, err
195 }
196 defer file.Close()
197
198 maps := []*ProcMap{}
199 scan := bufio.NewScanner(file)
200
201 for scan.Scan() {
202 m, err := parseProcMap(scan.Text())
203 if err != nil {
204 return nil, err
205 }
206
207 maps = append(maps, m)
208 }
209
210 return maps, nil
211 }
212
View as plain text