psgo.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. // Copyright 2021 Jeffery H. Johnson <trnsz@pobox.com>
  2. // Copyright 2021 Gridfinity, LLC.
  3. // Copyright 2020 The psgo authors.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may 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, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. // Package gfpsgo is a ps (1) AIX-format compatible golang library extended
  17. // with various descriptors useful for displaying container-related data.
  18. //
  19. // The idea behind the library is to provide an easy to use way of extracting
  20. // process-related data, just as ps (1) does. The problem when using ps (1)
  21. // is that the ps format strings split columns with whitespaces, making the
  22. // output nearly impossible to parse. It also adds some jitter as we have to
  23. // fork and execute ps either in the container or filter the output
  24. // afterwards, further limiting applicability.
  25. //
  26. package gfpsgo // import "github.com/johnsonjh/gfpsgo"
  27. import (
  28. "fmt"
  29. "io/ioutil"
  30. "os"
  31. "runtime"
  32. "sort"
  33. "strconv"
  34. "strings"
  35. "sync"
  36. "github.com/johnsonjh/gfpsgo/internal/capabilities"
  37. "github.com/johnsonjh/gfpsgo/internal/dev"
  38. "github.com/johnsonjh/gfpsgo/internal/proc"
  39. "github.com/johnsonjh/gfpsgo/internal/process"
  40. "github.com/pkg/errors"
  41. "golang.org/x/sys/unix"
  42. )
  43. // IDMap specifies a mapping range from the host to the container IDs.
  44. type IDMap struct {
  45. // ContainerID is the first ID in the container.
  46. ContainerID int
  47. // HostID is the first ID in the host.
  48. HostID int
  49. // Size specifies how long is the range. e.g. 1 means a single user
  50. // is mapped.
  51. Size int
  52. }
  53. // JoinNamespaceOpts specifies different options for joining the specified
  54. // namespaces.
  55. type JoinNamespaceOpts struct {
  56. // UIDMap specifies a mapping for UIDs in the container. If specified
  57. // huser will perform the reverse mapping.
  58. UIDMap []IDMap
  59. // GIDMap specifies a mapping for GIDs in the container. If specified
  60. // hgroup will perform the reverse mapping.
  61. GIDMap []IDMap
  62. // FillMappings specified whether UIDMap and GIDMap must be initialized
  63. // with the current user namespace.
  64. FillMappings bool
  65. }
  66. type psContext struct {
  67. // Processes in the container.
  68. containersProcesses []*process.Process
  69. // Processes on the host. Used to map those to the ones running in the
  70. // container.
  71. hostProcesses []*process.Process
  72. // tty and pty devices.
  73. ttys *[]dev.TTY
  74. // Various options
  75. opts *JoinNamespaceOpts
  76. }
  77. // processFunc is used to map a given aixFormatDescriptor to a corresponding
  78. // function extracting the desired data from a process.
  79. type processFunc func(*process.Process, *psContext) (string, error)
  80. // aixFormatDescriptor as mentioned in the ps(1) manpage. A given descriptor
  81. // can either be specified via its code (e.g., "%C") or its normal
  82. // representation
  83. // (e.g., "pcpu") and will be printed under its corresponding header (e.g,
  84. // "%CPU").
  85. type aixFormatDescriptor struct {
  86. // code descriptor in the short form (e.g., "%C").
  87. code string
  88. // normal descriptor in the long form (e.g., "pcpu").
  89. normal string
  90. // header of the descriptor (e.g., "%CPU").
  91. header string
  92. // onHost controls if data of the corresponding host processes will be
  93. // extracted as well.
  94. onHost bool
  95. // procFN points to the corresponding method to extract the desired data.
  96. procFn processFunc
  97. }
  98. // findID converts the specified id to the host mapping
  99. func findID(
  100. idStr string,
  101. mapping []IDMap,
  102. lookupFunc func(uid string) (string, error),
  103. overflowFile string,
  104. ) (string, error) {
  105. if len(mapping) == 0 {
  106. return idStr, nil
  107. }
  108. id, err := strconv.ParseInt(idStr, 10, 0)
  109. if err != nil {
  110. return "", errors.Wrapf(err, "cannot parse %s", idStr)
  111. }
  112. for _, m := range mapping {
  113. if int(id) >= m.ContainerID && int(id) < m.ContainerID+m.Size {
  114. user := fmt.Sprintf("%d", m.HostID+(int(id)-m.ContainerID))
  115. return lookupFunc(user)
  116. }
  117. }
  118. // User not found, read the overflow
  119. overflow, err := ioutil.ReadFile(overflowFile)
  120. if err != nil {
  121. return "", errors.Wrapf(err, "cannot read %s", overflowFile)
  122. }
  123. return string(overflow), nil
  124. }
  125. // translateDescriptors parses the descriptors and returns a correspodning
  126. // slice of
  127. // aixFormatDescriptors. Descriptors can be specified in the normal and in
  128. // the
  129. // code form (if supported). If the descriptors slice is empty, the
  130. // `DefaultDescriptors` is used.
  131. func translateDescriptors(
  132. descriptors []string,
  133. ) ([]aixFormatDescriptor, error) {
  134. if len(descriptors) == 0 {
  135. descriptors = DefaultDescriptors
  136. }
  137. formatDescriptors := []aixFormatDescriptor{}
  138. for _, d := range descriptors {
  139. d = strings.TrimSpace(d)
  140. found := false
  141. for _, aix := range aixFormatDescriptors {
  142. if d == aix.code || d == aix.normal {
  143. formatDescriptors = append(formatDescriptors, aix)
  144. found = true
  145. }
  146. }
  147. if !found {
  148. return nil, errors.Wrapf(ErrUnknownDescriptor, "'%s'", d)
  149. }
  150. }
  151. return formatDescriptors, nil
  152. }
  153. var (
  154. // DefaultDescriptors is the `ps -ef` compatible default format.
  155. DefaultDescriptors = []string{
  156. "user",
  157. "pid",
  158. "ppid",
  159. "pcpu",
  160. "etime",
  161. "tty",
  162. "time",
  163. "args",
  164. }
  165. // ErrUnknownDescriptor is returned when an unknown descriptor is parsed.
  166. ErrUnknownDescriptor = errors.New("unknown descriptor")
  167. aixFormatDescriptors = []aixFormatDescriptor{
  168. {
  169. code: "%C",
  170. normal: "pcpu",
  171. header: "%CPU",
  172. procFn: processPCPU,
  173. },
  174. {
  175. code: "%G",
  176. normal: "group",
  177. header: "GROUP",
  178. procFn: processGROUP,
  179. },
  180. {
  181. code: "%P",
  182. normal: "ppid",
  183. header: "PPID",
  184. procFn: processPPID,
  185. },
  186. {
  187. code: "%U",
  188. normal: "user",
  189. header: "USER",
  190. procFn: processUSER,
  191. },
  192. {
  193. code: "%a",
  194. normal: "args",
  195. header: "COMMAND",
  196. procFn: processARGS,
  197. },
  198. {
  199. code: "%c",
  200. normal: "comm",
  201. header: "COMMAND",
  202. procFn: processCOMM,
  203. },
  204. {
  205. code: "%g",
  206. normal: "rgroup",
  207. header: "RGROUP",
  208. procFn: processRGROUP,
  209. },
  210. {
  211. code: "%n",
  212. normal: "nice",
  213. header: "NI",
  214. procFn: processNICE,
  215. },
  216. {
  217. code: "%p",
  218. normal: "pid",
  219. header: "PID",
  220. procFn: processPID,
  221. },
  222. {
  223. code: "%r",
  224. normal: "pgid",
  225. header: "PGID",
  226. procFn: processPGID,
  227. },
  228. {
  229. code: "%t",
  230. normal: "etime",
  231. header: "ELAPSED",
  232. procFn: processETIME,
  233. },
  234. {
  235. code: "%u",
  236. normal: "ruser",
  237. header: "RUSER",
  238. procFn: processRUSER,
  239. },
  240. {
  241. code: "%x",
  242. normal: "time",
  243. header: "TIME",
  244. procFn: processTIME,
  245. },
  246. {
  247. code: "%y",
  248. normal: "tty",
  249. header: "TTY",
  250. procFn: processTTY,
  251. },
  252. {
  253. code: "%z",
  254. normal: "vsz",
  255. header: "VSZ",
  256. procFn: processVSZ,
  257. },
  258. {
  259. normal: "capamb",
  260. header: "AMBIENT CAPS",
  261. procFn: processCAPAMB,
  262. },
  263. {
  264. normal: "capinh",
  265. header: "INHERITED CAPS",
  266. procFn: processCAPINH,
  267. },
  268. {
  269. normal: "capprm",
  270. header: "PERMITTED CAPS",
  271. procFn: processCAPPRM,
  272. },
  273. {
  274. normal: "capeff",
  275. header: "EFFECTIVE CAPS",
  276. procFn: processCAPEFF,
  277. },
  278. {
  279. normal: "capbnd",
  280. header: "BOUNDING CAPS",
  281. procFn: processCAPBND,
  282. },
  283. {
  284. normal: "seccomp",
  285. header: "SECCOMP",
  286. procFn: processSECCOMP,
  287. },
  288. {
  289. normal: "label",
  290. header: "LABEL",
  291. procFn: processLABEL,
  292. },
  293. {
  294. normal: "hpid",
  295. header: "HPID",
  296. onHost: true,
  297. procFn: processHPID,
  298. },
  299. {
  300. normal: "huser",
  301. header: "HUSER",
  302. onHost: true,
  303. procFn: processHUSER,
  304. },
  305. {
  306. normal: "hgroup",
  307. header: "HGROUP",
  308. onHost: true,
  309. procFn: processHGROUP,
  310. },
  311. {
  312. normal: "rss",
  313. header: "RSS",
  314. procFn: processRSS,
  315. },
  316. {
  317. normal: "state",
  318. header: "STATE",
  319. procFn: processState,
  320. },
  321. {
  322. normal: "stime",
  323. header: "STIME",
  324. procFn: processStartTime,
  325. },
  326. }
  327. )
  328. // ListDescriptors returns a string slice of all supported AIX format
  329. // descriptors in the normal form.
  330. func ListDescriptors() (list []string) {
  331. for _, d := range aixFormatDescriptors {
  332. list = append(list, d.normal)
  333. }
  334. sort.Strings(list)
  335. return
  336. }
  337. // JoinNamespaceAndProcessInfo has the same semantics as ProcessInfo but
  338. // joins
  339. // the mount namespace of the specified pid before extracting data from
  340. // `/proc`.
  341. func JoinNamespaceAndProcessInfo(
  342. pid string,
  343. descriptors []string,
  344. ) ([][]string, error) {
  345. return JoinNamespaceAndProcessInfoWithOptions(
  346. pid,
  347. descriptors,
  348. &JoinNamespaceOpts{},
  349. )
  350. }
  351. // ReadMappings ...
  352. func ReadMappings(path string) ([]IDMap, error) {
  353. mappings, err := proc.ReadMappings(path)
  354. if err != nil {
  355. return nil, err
  356. }
  357. var res []IDMap
  358. for _, i := range mappings {
  359. m := IDMap{
  360. ContainerID: i.ContainerID,
  361. HostID: i.HostID,
  362. Size: i.Size,
  363. }
  364. res = append(res, m)
  365. }
  366. return res, nil
  367. }
  368. func contextFromOptions(options *JoinNamespaceOpts) (*psContext, error) {
  369. ctx := new(psContext)
  370. ctx.opts = options
  371. if ctx.opts != nil && ctx.opts.FillMappings {
  372. uidMappings, err := ReadMappings("/proc/self/uid_map")
  373. if err != nil {
  374. return nil, err
  375. }
  376. gidMappings, err := ReadMappings("/proc/self/gid_map")
  377. if err != nil {
  378. return nil, err
  379. }
  380. ctx.opts.UIDMap = uidMappings
  381. ctx.opts.GIDMap = gidMappings
  382. ctx.opts.FillMappings = false
  383. }
  384. return ctx, nil
  385. }
  386. // JoinNamespaceAndProcessInfoWithOptions has the same semantics as
  387. // ProcessInfo but joins
  388. // the mount namespace of the specified pid before extracting data from
  389. // `/proc`.
  390. func JoinNamespaceAndProcessInfoWithOptions(
  391. pid string,
  392. descriptors []string,
  393. options *JoinNamespaceOpts,
  394. ) ([][]string, error) {
  395. var (
  396. data [][]string
  397. dataErr error
  398. wg sync.WaitGroup
  399. )
  400. aixDescriptors, err := translateDescriptors(descriptors)
  401. if err != nil {
  402. return nil, err
  403. }
  404. ctx, err := contextFromOptions(options)
  405. if err != nil {
  406. return nil, err
  407. }
  408. // extract data from host processes only on-demand / when at least one
  409. // of the specified descriptors requires host data
  410. for _, d := range aixDescriptors {
  411. if d.onHost {
  412. ctx.hostProcesses, err = hostProcesses(pid)
  413. if err != nil {
  414. return nil, err
  415. }
  416. break
  417. }
  418. }
  419. wg.Add(1)
  420. go func() {
  421. defer wg.Done()
  422. runtime.LockOSThread()
  423. // extract user namespaces prior to joining the mount namespace
  424. currentUserNs, err := proc.ParseUserNamespace("self")
  425. if err != nil {
  426. dataErr = errors.Wrapf(err, "error determining user namespace")
  427. return
  428. }
  429. pidUserNs, err := proc.ParseUserNamespace(pid)
  430. if err != nil {
  431. dataErr = errors.Wrapf(
  432. err,
  433. "error determining user namespace of PID %s",
  434. pid,
  435. )
  436. }
  437. // join the mount namespace of pid
  438. fd, err := os.Open(fmt.Sprintf("/proc/%s/ns/mnt", pid))
  439. if err != nil {
  440. dataErr = err
  441. return
  442. }
  443. defer fd.Close()
  444. // create a new mountns on the current thread
  445. if err = unix.Unshare(unix.CLONE_NEWNS); err != nil {
  446. dataErr = err
  447. return
  448. }
  449. if err := unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS); err != nil {
  450. dataErr = err
  451. return
  452. }
  453. // extract all pids mentioned in pid's mount namespace
  454. pids, err := proc.GetPIDs()
  455. if err != nil {
  456. dataErr = err
  457. return
  458. }
  459. // join the user NS if the pid's user NS is different
  460. // to the caller's user NS.
  461. joinUserNS := currentUserNs != pidUserNs
  462. ctx.containersProcesses, err = process.FromPIDs(pids, joinUserNS)
  463. if err != nil {
  464. dataErr = err
  465. return
  466. }
  467. data, dataErr = processDescriptors(aixDescriptors, ctx)
  468. }()
  469. wg.Wait()
  470. return data, dataErr
  471. }
  472. // JoinNamespaceAndProcessInfoByPidsWithOptions has similar semantics to
  473. // JoinNamespaceAndProcessInfo and avoids duplicate entries by joining a
  474. // giving
  475. // PID namespace only once.
  476. func JoinNamespaceAndProcessInfoByPidsWithOptions(
  477. pids, descriptors []string,
  478. options *JoinNamespaceOpts,
  479. ) ([][]string, error) {
  480. // Extracting data from processes that share the same PID namespace
  481. // would yield duplicate results. Avoid that by extracting data only
  482. // from the first process in `pids` from a given PID namespace.
  483. // `nsMap` is used for quick lookups if a given PID namespace is
  484. // already covered, `pidList` is used to preserve the order which is
  485. // not guaranteed by nondeterministic maps in golang.
  486. nsMap := make(map[string]bool)
  487. pidList := []string{}
  488. for _, pid := range pids {
  489. ns, err := proc.ParsePIDNamespace(pid)
  490. if err != nil {
  491. if os.IsNotExist(errors.Cause(err)) {
  492. // catch race conditions
  493. continue
  494. }
  495. return nil, errors.Wrapf(err, "error extracting PID namespace")
  496. }
  497. if _, exists := nsMap[ns]; !exists {
  498. nsMap[ns] = true
  499. pidList = append(pidList, pid)
  500. }
  501. }
  502. data := [][]string{}
  503. for i, pid := range pidList {
  504. pidData, err := JoinNamespaceAndProcessInfoWithOptions(
  505. pid,
  506. descriptors,
  507. options,
  508. )
  509. if os.IsNotExist(errors.Cause(err)) {
  510. // catch race conditions
  511. continue
  512. }
  513. if err != nil {
  514. return nil, err
  515. }
  516. if i == 0 {
  517. data = append(data, pidData[0])
  518. }
  519. data = append(data, pidData[1:]...)
  520. }
  521. return data, nil
  522. }
  523. // JoinNamespaceAndProcessInfoByPids has similar semantics to
  524. // JoinNamespaceAndProcessInfo and avoids duplicate entries by joining a
  525. // giving
  526. // PID namespace only once.
  527. func JoinNamespaceAndProcessInfoByPids(
  528. pids, descriptors []string,
  529. ) ([][]string, error) {
  530. return JoinNamespaceAndProcessInfoByPidsWithOptions(
  531. pids,
  532. descriptors,
  533. &JoinNamespaceOpts{},
  534. )
  535. }
  536. // ProcessInfo returns the process information of all processes in the
  537. // current
  538. // mount namespace. The input format must be a comma-separated list of
  539. // supported AIX format descriptors. If the input string is empty, the
  540. // `DefaultDescriptors` is used.
  541. // The return value is an array of tab-separated strings, to easily use the
  542. // output for column-based formatting (e.g., with the `text/tabwriter`
  543. // package).
  544. func ProcessInfo(descriptors []string) ([][]string, error) {
  545. pids, err := proc.GetPIDs()
  546. if err != nil {
  547. return nil, err
  548. }
  549. return ProcessInfoByPids(pids, descriptors)
  550. }
  551. // ProcessInfoByPids is like ProcessInfo, but the process information
  552. // returned
  553. // is limited to a list of user specified PIDs.
  554. func ProcessInfoByPids(pids, descriptors []string) ([][]string, error) {
  555. aixDescriptors, err := translateDescriptors(descriptors)
  556. if err != nil {
  557. return nil, err
  558. }
  559. ctx, err := contextFromOptions(nil)
  560. if err != nil {
  561. return nil, err
  562. }
  563. ctx.containersProcesses, err = process.FromPIDs(pids, false)
  564. if err != nil {
  565. return nil, err
  566. }
  567. return processDescriptors(aixDescriptors, ctx)
  568. }
  569. // hostProcesses returns all processes running in the current namespace.
  570. func hostProcesses(pid string) ([]*process.Process, error) {
  571. // get processes
  572. pids, err := proc.GetPIDsFromCgroup(pid)
  573. if err != nil {
  574. return nil, err
  575. }
  576. processes, err := process.FromPIDs(pids, false)
  577. if err != nil {
  578. return nil, err
  579. }
  580. // set the additional host data
  581. for _, p := range processes {
  582. if err := p.SetHostData(); err != nil {
  583. return nil, err
  584. }
  585. }
  586. return processes, nil
  587. }
  588. // processDescriptors calls each `procFn` of all formatDescriptors on each
  589. // process and returns an array of tab-separated strings.
  590. func processDescriptors(
  591. formatDescriptors []aixFormatDescriptor,
  592. ctx *psContext,
  593. ) ([][]string, error) {
  594. data := [][]string{}
  595. // create header
  596. header := []string{}
  597. for _, desc := range formatDescriptors {
  598. header = append(header, desc.header)
  599. }
  600. data = append(data, header)
  601. // dispatch all descriptor functions on each process
  602. for _, proc := range ctx.containersProcesses {
  603. pData := []string{}
  604. for _, desc := range formatDescriptors {
  605. dataStr, err := desc.procFn(proc, ctx)
  606. if err != nil {
  607. return nil, err
  608. }
  609. pData = append(pData, dataStr)
  610. }
  611. data = append(data, pData)
  612. }
  613. return data, nil
  614. }
  615. // findHostProcess returns the corresponding process from `hostProcesses` or
  616. // nil if non is found.
  617. func findHostProcess(p *process.Process, ctx *psContext) *process.Process {
  618. for _, hp := range ctx.hostProcesses {
  619. // We expect the host process to be in another namespace, so
  620. // /proc/$pid/status.NSpid must have at least two entries.
  621. if len(hp.Status.NSpid) < 2 {
  622. continue
  623. }
  624. // The process' PID must match the one in the NS of the host
  625. // process and both must share the same pid NS.
  626. if p.Pid == hp.Status.NSpid[1] && p.PidNS == hp.PidNS {
  627. return hp
  628. }
  629. }
  630. return nil
  631. }
  632. // processGROUP returns the effective group ID of the process. This will be
  633. // the textual group ID, if it can be optained, or a decimal representation
  634. // otherwise.
  635. func processGROUP(p *process.Process, _ *psContext) (string, error) {
  636. return process.LookupGID(p.Status.Gids[1])
  637. }
  638. // processRGROUP returns the real group ID of the process. This will be
  639. // the textual group ID, if it can be optained, or a decimal representation
  640. // otherwise.
  641. func processRGROUP(p *process.Process, _ *psContext) (string, error) {
  642. return process.LookupGID(p.Status.Gids[0])
  643. }
  644. // processPPID returns the parent process ID of process p.
  645. func processPPID(p *process.Process, _ *psContext) (string, error) {
  646. return p.Status.PPid, nil
  647. }
  648. // processUSER returns the effective user name of the process. This will be
  649. // the textual user ID, if it can be optained, or a decimal representation
  650. // otherwise.
  651. func processUSER(p *process.Process, _ *psContext) (string, error) {
  652. return process.LookupUID(p.Status.Uids[1])
  653. }
  654. // processRUSER returns the effective user name of the process. This will be
  655. // the textual user ID, if it can be optained, or a decimal representation
  656. // otherwise.
  657. func processRUSER(p *process.Process, _ *psContext) (string, error) {
  658. return process.LookupUID(p.Status.Uids[0])
  659. }
  660. // processName returns the name of process p in the format "[$name]".
  661. func processName(p *process.Process, _ *psContext) (string, error) {
  662. return fmt.Sprintf("[%s]", p.Status.Name), nil
  663. }
  664. // processARGS returns the command of p with all its arguments.
  665. func processARGS(p *process.Process, ctx *psContext) (string, error) {
  666. // ps (1) returns "[$name]" if command/args are empty
  667. if p.CmdLine[0] == "" {
  668. return processName(p, ctx)
  669. }
  670. return strings.Join(p.CmdLine, " "), nil
  671. }
  672. // processCOMM returns the command name (i.e., executable name) of process p.
  673. func processCOMM(p *process.Process, _ *psContext) (string, error) {
  674. return p.Stat.Comm, nil
  675. }
  676. // processNICE returns the nice value of process p.
  677. func processNICE(p *process.Process, _ *psContext) (string, error) {
  678. return p.Stat.Nice, nil
  679. }
  680. // processPID returns the process ID of process p.
  681. func processPID(p *process.Process, _ *psContext) (string, error) {
  682. return p.Pid, nil
  683. }
  684. // processPGID returns the process group ID of process p.
  685. func processPGID(p *process.Process, _ *psContext) (string, error) {
  686. return p.Stat.Pgrp, nil
  687. }
  688. // processPCPU returns how many percent of the CPU time process p uses as
  689. // a three digit float as string.
  690. func processPCPU(p *process.Process, _ *psContext) (string, error) {
  691. elapsed, err := p.ElapsedTime()
  692. if err != nil {
  693. return "", err
  694. }
  695. cpu, err := p.CPUTime()
  696. if err != nil {
  697. return "", err
  698. }
  699. pcpu := 100 * cpu.Seconds() / elapsed.Seconds()
  700. return strconv.FormatFloat(pcpu, 'f', 3, 64), nil
  701. }
  702. // processETIME returns the elapsed time since the process was started.
  703. func processETIME(p *process.Process, _ *psContext) (string, error) {
  704. elapsed, err := p.ElapsedTime()
  705. if err != nil {
  706. return "", nil
  707. }
  708. return fmt.Sprintf("%v", elapsed), nil
  709. }
  710. // processTIME returns the cumulative CPU time of process p.
  711. func processTIME(p *process.Process, _ *psContext) (string, error) {
  712. cpu, err := p.CPUTime()
  713. if err != nil {
  714. return "", err
  715. }
  716. return fmt.Sprintf("%v", cpu), nil
  717. }
  718. // processStartTime returns the start time of process p.
  719. func processStartTime(p *process.Process, _ *psContext) (string, error) {
  720. sTime, err := p.StartTime()
  721. if err != nil {
  722. return "", err
  723. }
  724. return fmt.Sprintf("%v", sTime), nil
  725. }
  726. // processTTY returns the controlling tty (terminal) of process p.
  727. func processTTY(p *process.Process, ctx *psContext) (string, error) {
  728. ttyNr, err := strconv.ParseUint(p.Stat.TtyNr, 10, 64)
  729. if err != nil {
  730. return "", nil
  731. }
  732. tty, err := dev.FindTTY(ttyNr, ctx.ttys)
  733. if err != nil {
  734. return "", nil
  735. }
  736. ttyS := "?"
  737. if tty != nil {
  738. ttyS = strings.TrimPrefix(tty.Path, "/dev/")
  739. }
  740. return ttyS, nil
  741. }
  742. // processVSZ returns the virtual memory size of process p in KiB (1024-byte
  743. // units).
  744. func processVSZ(p *process.Process, _ *psContext) (string, error) {
  745. vmsize, err := strconv.Atoi(p.Stat.Vsize)
  746. if err != nil {
  747. return "", err
  748. }
  749. return fmt.Sprintf("%d", vmsize/1024), nil
  750. }
  751. // parseCAP parses cap (a string bit mask) and returns the associated set of
  752. // capabilities. If all capabilities are set, "full" is returned. If no
  753. // capability is enabled, "none" is returned.
  754. func parseCAP(capz string) (string, error) {
  755. mask, err := strconv.ParseUint(capz, 16, 64)
  756. if err != nil {
  757. return "", err
  758. }
  759. if mask == capabilities.FullCAPs {
  760. return "full", nil
  761. }
  762. caps := capabilities.TranslateMask(mask)
  763. if len(caps) == 0 {
  764. return "none", nil
  765. }
  766. sort.Strings(caps)
  767. return strings.Join(caps, ","), nil
  768. }
  769. // processCAPAMB returns the set of ambient capabilities associated with
  770. // process p. If all capabilities are set, "full" is returned. If no
  771. // capability is enabled, "none" is returned.
  772. func processCAPAMB(p *process.Process, _ *psContext) (string, error) {
  773. return parseCAP(p.Status.CapAmb)
  774. }
  775. // processCAPINH returns the set of inheritable capabilities associated with
  776. // process p. If all capabilities are set, "full" is returned. If no
  777. // capability is enabled, "none" is returned.
  778. func processCAPINH(p *process.Process, _ *psContext) (string, error) {
  779. return parseCAP(p.Status.CapInh)
  780. }
  781. // processCAPPRM returns the set of permitted capabilities associated with
  782. // process p. If all capabilities are set, "full" is returned. If no
  783. // capability is enabled, "none" is returned.
  784. func processCAPPRM(p *process.Process, _ *psContext) (string, error) {
  785. return parseCAP(p.Status.CapPrm)
  786. }
  787. // processCAPEFF returns the set of effective capabilities associated with
  788. // process p. If all capabilities are set, "full" is returned. If no
  789. // capability is enabled, "none" is returned.
  790. func processCAPEFF(p *process.Process, _ *psContext) (string, error) {
  791. return parseCAP(p.Status.CapEff)
  792. }
  793. // processCAPBND returns the set of bounding capabilities associated with
  794. // process p. If all capabilities are set, "full" is returned. If no
  795. // capability is enabled, "none" is returned.
  796. func processCAPBND(p *process.Process, _ *psContext) (string, error) {
  797. return parseCAP(p.Status.CapBnd)
  798. }
  799. // processSECCOMP returns the seccomp mode of the process (i.e., disabled,
  800. // strict or filter) or "?" if /proc/$pid/status.seccomp has a unknown value.
  801. func processSECCOMP(p *process.Process, _ *psContext) (string, error) {
  802. switch p.Status.Seccomp {
  803. case "0":
  804. return "disabled", nil
  805. case "1":
  806. return "strict", nil
  807. case "2":
  808. return "filter", nil
  809. default:
  810. return "?", nil
  811. }
  812. }
  813. // processLABEL returns the process label of process p or "?" if the system
  814. // doesn't support labeling.
  815. func processLABEL(p *process.Process, _ *psContext) (string, error) {
  816. return p.Label, nil
  817. }
  818. // processHPID returns the PID of the corresponding host process of the
  819. // (container) or "?" if no corresponding process could be found.
  820. func processHPID(p *process.Process, ctx *psContext) (string, error) {
  821. if hp := findHostProcess(p, ctx); hp != nil {
  822. return hp.Pid, nil
  823. }
  824. return "?", nil
  825. }
  826. // processHUSER returns the effective user ID of the corresponding host
  827. // process
  828. // of the (container) or "?" if no corresponding process could be found.
  829. func processHUSER(p *process.Process, ctx *psContext) (string, error) {
  830. if hp := findHostProcess(p, ctx); hp != nil {
  831. if ctx.opts != nil && len(ctx.opts.UIDMap) > 0 {
  832. return findID(
  833. hp.Status.Uids[1],
  834. ctx.opts.UIDMap,
  835. process.LookupUID,
  836. "/proc/sys/fs/overflowuid",
  837. )
  838. }
  839. return hp.Huser, nil
  840. }
  841. return "?", nil
  842. }
  843. // processHGROUP returns the effective group ID of the corresponding host
  844. // process of the (container) or "?" if no corresponding process could be
  845. // found.
  846. func processHGROUP(p *process.Process, ctx *psContext) (string, error) {
  847. if hp := findHostProcess(p, ctx); hp != nil {
  848. if ctx.opts != nil && len(ctx.opts.GIDMap) > 0 {
  849. return findID(
  850. hp.Status.Gids[1],
  851. ctx.opts.GIDMap,
  852. process.LookupGID,
  853. "/proc/sys/fs/overflowgid",
  854. )
  855. }
  856. return hp.Hgroup, nil
  857. }
  858. return "?", nil
  859. }
  860. // processRSS returns the resident set size of process p in KiB (1024-byte
  861. // units).
  862. func processRSS(p *process.Process, _ *psContext) (string, error) {
  863. if p.Status.VMRSS == "" {
  864. // probably a kernel thread
  865. return "0", nil
  866. }
  867. return p.Status.VMRSS, nil
  868. }
  869. // processState returns the process state of process p.
  870. func processState(p *process.Process, _ *psContext) (string, error) {
  871. return p.Status.State, nil
  872. }