1// Licensed to Elasticsearch B.V. under one or more contributor 2// license agreements. See the NOTICE file distributed with 3// this work for additional information regarding copyright 4// ownership. Elasticsearch B.V. licenses this file to you under 5// the Apache License, Version 2.0 (the "License"); you may 6// not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, 12// software distributed under the License is distributed on an 13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14// KIND, either express or implied. See the License for the 15// specific language governing permissions and limitations 16// under the License. 17 18package linux 19 20import ( 21 "bytes" 22 "io/ioutil" 23 "os" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/prometheus/procfs" 29 30 "github.com/elastic/go-sysinfo/types" 31) 32 33const userHz = 100 34 35func (s linuxSystem) Processes() ([]types.Process, error) { 36 procs, err := s.procFS.AllProcs() 37 if err != nil { 38 return nil, err 39 } 40 41 processes := make([]types.Process, 0, len(procs)) 42 for _, proc := range procs { 43 processes = append(processes, &process{Proc: proc, fs: s.procFS}) 44 } 45 return processes, nil 46} 47 48func (s linuxSystem) Process(pid int) (types.Process, error) { 49 proc, err := s.procFS.NewProc(pid) 50 if err != nil { 51 return nil, err 52 } 53 54 return &process{Proc: proc, fs: s.procFS}, nil 55} 56 57func (s linuxSystem) Self() (types.Process, error) { 58 proc, err := s.procFS.Self() 59 if err != nil { 60 return nil, err 61 } 62 63 return &process{Proc: proc, fs: s.procFS}, nil 64} 65 66type process struct { 67 procfs.Proc 68 fs procFS 69 info *types.ProcessInfo 70} 71 72func (p *process) PID() int { 73 return p.Proc.PID 74} 75 76func (p *process) Parent() (types.Process, error) { 77 info, err := p.Info() 78 if err != nil { 79 return nil, err 80 } 81 82 proc, err := p.fs.NewProc(info.PPID) 83 if err != nil { 84 return nil, err 85 } 86 87 return &process{Proc: proc, fs: p.fs}, nil 88} 89 90func (p *process) path(pa ...string) string { 91 return p.fs.path(append([]string{strconv.Itoa(p.PID())}, pa...)...) 92} 93 94func (p *process) CWD() (string, error) { 95 // TODO: add CWD to procfs 96 cwd, err := os.Readlink(p.path("cwd")) 97 if os.IsNotExist(err) { 98 return "", nil 99 } 100 101 return cwd, err 102} 103 104func (p *process) Info() (types.ProcessInfo, error) { 105 if p.info != nil { 106 return *p.info, nil 107 } 108 109 stat, err := p.NewStat() 110 if err != nil { 111 return types.ProcessInfo{}, err 112 } 113 114 exe, err := p.Executable() 115 if err != nil { 116 return types.ProcessInfo{}, err 117 } 118 119 args, err := p.CmdLine() 120 if err != nil { 121 return types.ProcessInfo{}, err 122 } 123 124 cwd, err := p.CWD() 125 if err != nil { 126 return types.ProcessInfo{}, err 127 } 128 129 bootTime, err := bootTime(p.fs.FS) 130 if err != nil { 131 return types.ProcessInfo{}, err 132 } 133 134 p.info = &types.ProcessInfo{ 135 Name: stat.Comm, 136 PID: p.PID(), 137 PPID: stat.PPID, 138 CWD: cwd, 139 Exe: exe, 140 Args: args, 141 StartTime: bootTime.Add(ticksToDuration(stat.Starttime)), 142 } 143 144 return *p.info, nil 145} 146 147func (p *process) Memory() (types.MemoryInfo, error) { 148 stat, err := p.NewStat() 149 if err != nil { 150 return types.MemoryInfo{}, err 151 } 152 153 return types.MemoryInfo{ 154 Resident: uint64(stat.ResidentMemory()), 155 Virtual: uint64(stat.VirtualMemory()), 156 }, nil 157} 158 159func (p *process) CPUTime() (types.CPUTimes, error) { 160 stat, err := p.NewStat() 161 if err != nil { 162 return types.CPUTimes{}, err 163 } 164 165 return types.CPUTimes{ 166 User: ticksToDuration(uint64(stat.UTime)), 167 System: ticksToDuration(uint64(stat.STime)), 168 }, nil 169} 170 171// OpenHandles returns the list of open file descriptors of the process. 172func (p *process) OpenHandles() ([]string, error) { 173 return p.Proc.FileDescriptorTargets() 174} 175 176// OpenHandles returns the number of open file descriptors of the process. 177func (p *process) OpenHandleCount() (int, error) { 178 return p.Proc.FileDescriptorsLen() 179} 180 181func (p *process) Environment() (map[string]string, error) { 182 // TODO: add Environment to procfs 183 content, err := ioutil.ReadFile(p.path("environ")) 184 if err != nil { 185 return nil, err 186 } 187 188 env := map[string]string{} 189 pairs := bytes.Split(content, []byte{0}) 190 for _, kv := range pairs { 191 parts := bytes.SplitN(kv, []byte{'='}, 2) 192 if len(parts) != 2 { 193 continue 194 } 195 196 key := string(bytes.TrimSpace(parts[0])) 197 if key == "" { 198 continue 199 } 200 201 env[key] = string(parts[1]) 202 } 203 204 return env, nil 205} 206 207func (p *process) Seccomp() (*types.SeccompInfo, error) { 208 content, err := ioutil.ReadFile(p.path("status")) 209 if err != nil { 210 return nil, err 211 } 212 213 return readSeccompFields(content) 214} 215 216func (p *process) Capabilities() (*types.CapabilityInfo, error) { 217 content, err := ioutil.ReadFile(p.path("status")) 218 if err != nil { 219 return nil, err 220 } 221 222 return readCapabilities(content) 223} 224 225func (p *process) User() (types.UserInfo, error) { 226 content, err := ioutil.ReadFile(p.path("status")) 227 if err != nil { 228 return types.UserInfo{}, err 229 } 230 231 var user types.UserInfo 232 err = parseKeyValue(content, ":", func(key, value []byte) error { 233 // See proc(5) for the format of /proc/[pid]/status 234 switch string(key) { 235 case "Uid": 236 ids := strings.Split(string(value), "\t") 237 if len(ids) >= 3 { 238 user.UID = ids[0] 239 user.EUID = ids[1] 240 user.SUID = ids[2] 241 } 242 case "Gid": 243 ids := strings.Split(string(value), "\t") 244 if len(ids) >= 3 { 245 user.GID = ids[0] 246 user.EGID = ids[1] 247 user.SGID = ids[2] 248 } 249 } 250 return nil 251 }) 252 253 return user, nil 254} 255 256func ticksToDuration(ticks uint64) time.Duration { 257 seconds := float64(ticks) / float64(userHz) * float64(time.Second) 258 return time.Duration(int64(seconds)) 259} 260