master
go 132 lines 2.52 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package phpfpm
4
5 import (
6 "bufio"
7 "encoding/json"
8 "errors"
9 "io"
10 "strconv"
11 "strings"
12 )
13
14 type decoder func(r io.Reader, s *status) error
15
16 func decodeJSON(r io.Reader, s *status) error {
17 return json.NewDecoder(r).Decode(s)
18 }
19
20 func decodeText(r io.Reader, s *status) error {
21 parts := readParts(r)
22 if len(parts) == 0 {
23 return errors.New("invalid text format")
24 }
25
26 part, parts := parts[0], parts[1:]
27 if err := readStatus(part, s); err != nil {
28 return err
29 }
30
31 return readProcesses(parts, s)
32 }
33
34 func readParts(r io.Reader) [][]string {
35 sc := bufio.NewScanner(r)
36
37 var parts [][]string
38 var lines []string
39 for sc.Scan() {
40 line := strings.Trim(sc.Text(), "\r\n ")
41 // Split parts by star border
42 if strings.HasPrefix(line, "***") {
43 parts = append(parts, lines)
44 lines = []string{}
45 continue
46 }
47 // Skip empty lines
48 if line == "" {
49 continue
50 }
51 lines = append(lines, line)
52 }
53
54 if len(lines) > 0 {
55 parts = append(parts, lines)
56 }
57 return parts
58 }
59
60 func readStatus(data []string, s *status) error {
61 for _, line := range data {
62 key, val, err := parseLine(line)
63 if err != nil {
64 return err
65 }
66
67 switch key {
68 case "active processes":
69 s.Active = parseInt(val)
70 case "max active processes":
71 s.MaxActive = parseInt(val)
72 case "idle processes":
73 s.Idle = parseInt(val)
74 case "accepted conn":
75 s.Requests = parseInt(val)
76 case "max children reached":
77 s.Reached = parseInt(val)
78 case "slow requests":
79 s.Slow = parseInt(val)
80 }
81 }
82 return nil
83 }
84
85 func readProcesses(procs [][]string, s *status) error {
86 for _, part := range procs {
87 var proc proc
88 for _, line := range part {
89 key, val, err := parseLine(line)
90 if err != nil {
91 return err
92 }
93
94 switch key {
95 case "state":
96 proc.State = val
97 case "request duration":
98 proc.Duration = requestDuration(parseInt(val))
99 case "last request cpu":
100 proc.CPU = parseFloat(val)
101 case "last request memory":
102 proc.Memory = parseInt(val)
103 }
104 }
105 s.Processes = append(s.Processes, proc)
106 }
107 return nil
108 }
109
110 func parseLine(s string) (string, string, error) {
111 kv := strings.SplitN(s, ":", 2)
112 if len(kv) != 2 {
113 return "", "", errors.New("invalid text format line")
114 }
115 return strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]), nil
116 }
117
118 func parseInt(s string) int64 {
119 val, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64)
120 if err != nil {
121 return 0
122 }
123 return val
124 }
125
126 func parseFloat(s string) float64 {
127 val, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
128 if err != nil {
129 return 0
130 }
131 return val
132 }