You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

proc.go 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Copyright 2018 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package procfs
  14. import (
  15. "bytes"
  16. "errors"
  17. "fmt"
  18. "io"
  19. "os"
  20. "strconv"
  21. "strings"
  22. "github.com/prometheus/procfs/internal/util"
  23. )
  24. // Proc provides information about a running process.
  25. type Proc struct {
  26. // The process ID.
  27. PID int
  28. fs FS
  29. }
  30. // Procs represents a list of Proc structs.
  31. type Procs []Proc
  32. var (
  33. ErrFileParse = errors.New("Error Parsing File")
  34. ErrFileRead = errors.New("Error Reading File")
  35. ErrMountPoint = errors.New("Error Accessing Mount point")
  36. )
  37. func (p Procs) Len() int { return len(p) }
  38. func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  39. func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
  40. // Self returns a process for the current process read via /proc/self.
  41. func Self() (Proc, error) {
  42. fs, err := NewFS(DefaultMountPoint)
  43. if err != nil || errors.Unwrap(err) == ErrMountPoint {
  44. return Proc{}, err
  45. }
  46. return fs.Self()
  47. }
  48. // NewProc returns a process for the given pid under /proc.
  49. func NewProc(pid int) (Proc, error) {
  50. fs, err := NewFS(DefaultMountPoint)
  51. if err != nil {
  52. return Proc{}, err
  53. }
  54. return fs.Proc(pid)
  55. }
  56. // AllProcs returns a list of all currently available processes under /proc.
  57. func AllProcs() (Procs, error) {
  58. fs, err := NewFS(DefaultMountPoint)
  59. if err != nil {
  60. return Procs{}, err
  61. }
  62. return fs.AllProcs()
  63. }
  64. // Self returns a process for the current process.
  65. func (fs FS) Self() (Proc, error) {
  66. p, err := os.Readlink(fs.proc.Path("self"))
  67. if err != nil {
  68. return Proc{}, err
  69. }
  70. pid, err := strconv.Atoi(strings.Replace(p, string(fs.proc), "", -1))
  71. if err != nil {
  72. return Proc{}, err
  73. }
  74. return fs.Proc(pid)
  75. }
  76. // NewProc returns a process for the given pid.
  77. //
  78. // Deprecated: Use fs.Proc() instead.
  79. func (fs FS) NewProc(pid int) (Proc, error) {
  80. return fs.Proc(pid)
  81. }
  82. // Proc returns a process for the given pid.
  83. func (fs FS) Proc(pid int) (Proc, error) {
  84. if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
  85. return Proc{}, err
  86. }
  87. return Proc{PID: pid, fs: fs}, nil
  88. }
  89. // AllProcs returns a list of all currently available processes.
  90. func (fs FS) AllProcs() (Procs, error) {
  91. d, err := os.Open(fs.proc.Path())
  92. if err != nil {
  93. return Procs{}, err
  94. }
  95. defer d.Close()
  96. names, err := d.Readdirnames(-1)
  97. if err != nil {
  98. return Procs{}, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err)
  99. }
  100. p := Procs{}
  101. for _, n := range names {
  102. pid, err := strconv.ParseInt(n, 10, 64)
  103. if err != nil {
  104. continue
  105. }
  106. p = append(p, Proc{PID: int(pid), fs: fs})
  107. }
  108. return p, nil
  109. }
  110. // CmdLine returns the command line of a process.
  111. func (p Proc) CmdLine() ([]string, error) {
  112. data, err := util.ReadFileNoStat(p.path("cmdline"))
  113. if err != nil {
  114. return nil, err
  115. }
  116. if len(data) < 1 {
  117. return []string{}, nil
  118. }
  119. return strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))), nil
  120. }
  121. // Wchan returns the wchan (wait channel) of a process.
  122. func (p Proc) Wchan() (string, error) {
  123. f, err := os.Open(p.path("wchan"))
  124. if err != nil {
  125. return "", err
  126. }
  127. defer f.Close()
  128. data, err := io.ReadAll(f)
  129. if err != nil {
  130. return "", err
  131. }
  132. wchan := string(data)
  133. if wchan == "" || wchan == "0" {
  134. return "", nil
  135. }
  136. return wchan, nil
  137. }
  138. // Comm returns the command name of a process.
  139. func (p Proc) Comm() (string, error) {
  140. data, err := util.ReadFileNoStat(p.path("comm"))
  141. if err != nil {
  142. return "", err
  143. }
  144. return strings.TrimSpace(string(data)), nil
  145. }
  146. // Executable returns the absolute path of the executable command of a process.
  147. func (p Proc) Executable() (string, error) {
  148. exe, err := os.Readlink(p.path("exe"))
  149. if os.IsNotExist(err) {
  150. return "", nil
  151. }
  152. return exe, err
  153. }
  154. // Cwd returns the absolute path to the current working directory of the process.
  155. func (p Proc) Cwd() (string, error) {
  156. wd, err := os.Readlink(p.path("cwd"))
  157. if os.IsNotExist(err) {
  158. return "", nil
  159. }
  160. return wd, err
  161. }
  162. // RootDir returns the absolute path to the process's root directory (as set by chroot).
  163. func (p Proc) RootDir() (string, error) {
  164. rdir, err := os.Readlink(p.path("root"))
  165. if os.IsNotExist(err) {
  166. return "", nil
  167. }
  168. return rdir, err
  169. }
  170. // FileDescriptors returns the currently open file descriptors of a process.
  171. func (p Proc) FileDescriptors() ([]uintptr, error) {
  172. names, err := p.fileDescriptors()
  173. if err != nil {
  174. return nil, err
  175. }
  176. fds := make([]uintptr, len(names))
  177. for i, n := range names {
  178. fd, err := strconv.ParseInt(n, 10, 32)
  179. if err != nil {
  180. return nil, fmt.Errorf("%s: Cannot parse line: %v: %w", ErrFileParse, i, err)
  181. }
  182. fds[i] = uintptr(fd)
  183. }
  184. return fds, nil
  185. }
  186. // FileDescriptorTargets returns the targets of all file descriptors of a process.
  187. // If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
  188. func (p Proc) FileDescriptorTargets() ([]string, error) {
  189. names, err := p.fileDescriptors()
  190. if err != nil {
  191. return nil, err
  192. }
  193. targets := make([]string, len(names))
  194. for i, name := range names {
  195. target, err := os.Readlink(p.path("fd", name))
  196. if err == nil {
  197. targets[i] = target
  198. }
  199. }
  200. return targets, nil
  201. }
  202. // FileDescriptorsLen returns the number of currently open file descriptors of
  203. // a process.
  204. func (p Proc) FileDescriptorsLen() (int, error) {
  205. // Use fast path if available (Linux v6.2): https://github.com/torvalds/linux/commit/f1f1f2569901
  206. if p.fs.isReal {
  207. stat, err := os.Stat(p.path("fd"))
  208. if err != nil {
  209. return 0, err
  210. }
  211. size := stat.Size()
  212. if size > 0 {
  213. return int(size), nil
  214. }
  215. }
  216. fds, err := p.fileDescriptors()
  217. if err != nil {
  218. return 0, err
  219. }
  220. return len(fds), nil
  221. }
  222. // MountStats retrieves statistics and configuration for mount points in a
  223. // process's namespace.
  224. func (p Proc) MountStats() ([]*Mount, error) {
  225. f, err := os.Open(p.path("mountstats"))
  226. if err != nil {
  227. return nil, err
  228. }
  229. defer f.Close()
  230. return parseMountStats(f)
  231. }
  232. // MountInfo retrieves mount information for mount points in a
  233. // process's namespace.
  234. // It supplies information missing in `/proc/self/mounts` and
  235. // fixes various other problems with that file too.
  236. func (p Proc) MountInfo() ([]*MountInfo, error) {
  237. data, err := util.ReadFileNoStat(p.path("mountinfo"))
  238. if err != nil {
  239. return nil, err
  240. }
  241. return parseMountInfo(data)
  242. }
  243. func (p Proc) fileDescriptors() ([]string, error) {
  244. d, err := os.Open(p.path("fd"))
  245. if err != nil {
  246. return nil, err
  247. }
  248. defer d.Close()
  249. names, err := d.Readdirnames(-1)
  250. if err != nil {
  251. return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err)
  252. }
  253. return names, nil
  254. }
  255. func (p Proc) path(pa ...string) string {
  256. return p.fs.proc.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
  257. }
  258. // FileDescriptorsInfo retrieves information about all file descriptors of
  259. // the process.
  260. func (p Proc) FileDescriptorsInfo() (ProcFDInfos, error) {
  261. names, err := p.fileDescriptors()
  262. if err != nil {
  263. return nil, err
  264. }
  265. var fdinfos ProcFDInfos
  266. for _, n := range names {
  267. fdinfo, err := p.FDInfo(n)
  268. if err != nil {
  269. continue
  270. }
  271. fdinfos = append(fdinfos, *fdinfo)
  272. }
  273. return fdinfos, nil
  274. }
  275. // Schedstat returns task scheduling information for the process.
  276. func (p Proc) Schedstat() (ProcSchedstat, error) {
  277. contents, err := os.ReadFile(p.path("schedstat"))
  278. if err != nil {
  279. return ProcSchedstat{}, err
  280. }
  281. return parseProcSchedstat(string(contents))
  282. }