github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-mailq/lib/main.go (about) 1 //go:build linux 2 3 package mpmailq 4 5 import ( 6 "bufio" 7 "flag" 8 "fmt" 9 "io" 10 "os" 11 "os/exec" 12 "regexp" 13 "strconv" 14 "strings" 15 16 mp "github.com/mackerelio/go-mackerel-plugin-helper" 17 ) 18 19 type mailq struct { 20 command string 21 args []string 22 line int 23 pattern string 24 } 25 26 var mailqFormats = map[string]mailq{ 27 "postfix": { 28 command: "postqueue", 29 args: []string{"-p"}, 30 line: -1, 31 pattern: `-- \d+ Kbytes in (\d+) (?:Request|Requests)\.`, 32 }, 33 "qmail": { 34 command: "qmail-qstat", 35 pattern: `messages in queue: (\d+)`, 36 }, 37 "exim": { 38 command: "exim", 39 args: []string{"-bpc"}, 40 pattern: `(\d+)`, 41 }, 42 } 43 44 type plugin struct { 45 path string 46 mailq mailq 47 keyPrefix, labelPrefix string 48 } 49 50 func (format *mailq) parse(rd io.Reader) (count uint64, err error) { 51 l, err := getNthLine(rd, format.line) 52 if err != nil { 53 return 54 } 55 56 m := regexp.MustCompile(format.pattern).FindStringSubmatch(l) 57 if m != nil { 58 count, err = strconv.ParseUint(m[1], 10, 64) 59 } 60 61 return 62 } 63 64 func (p *plugin) fetchMailqCount() (count uint64, err error) { 65 66 var path string 67 if p.path != "" { 68 path = p.path 69 } else { 70 path, err = exec.LookPath(p.mailq.command) 71 if err != nil { 72 return 73 } 74 } 75 76 cmd := exec.Cmd{ 77 Path: path, 78 Args: append([]string{p.mailq.command}, p.mailq.args...), 79 } 80 81 if p.path != "" { 82 cmd.Path = p.path 83 } 84 85 stdout, err := cmd.StdoutPipe() 86 if err != nil { 87 return 88 } 89 90 err = cmd.Start() 91 if err != nil { 92 return 93 } 94 95 count, err = p.mailq.parse(stdout) 96 if err != nil { 97 cmd.Wait() // nolint 98 return 99 } 100 101 err = cmd.Wait() 102 return 103 } 104 105 func (p *plugin) FetchMetrics() (map[string]interface{}, error) { 106 count, err := p.fetchMailqCount() 107 if err != nil { 108 return nil, err 109 } 110 111 return map[string]interface{}{"count": count}, nil 112 } 113 114 func (p *plugin) GraphDefinition() map[string]mp.Graphs { 115 return map[string]mp.Graphs{ 116 p.keyPrefix: { 117 Label: p.labelPrefix + " Count", 118 Unit: "integer", 119 Metrics: []mp.Metrics{ 120 {Name: "count", Label: "count", Type: "uint64"}, 121 }, 122 }, 123 } 124 } 125 126 func getNthLine(rd io.Reader, nth int) (string, error) { 127 var result string 128 129 brd := bufio.NewReader(rd) 130 131 if nth >= 0 { // get nth-first line (0 is the first line) 132 for i := 0; ; i++ { 133 s, err := brd.ReadString('\n') 134 if err == nil || err == io.EOF { 135 if i == nth { 136 result = strings.TrimRight(s, "\n") 137 } 138 if err == io.EOF { 139 break 140 } 141 } else { 142 return "", err 143 } 144 } 145 } else { // get nth-last line (-1 is the last line, -2 is the second last line) 146 buffer := make([]string, -nth) 147 i := 0 148 for { 149 s, err := brd.ReadString('\n') 150 if err == nil || err == io.EOF { 151 if s != "" { 152 buffer[i%-nth] = strings.TrimRight(s, "\n") 153 i++ 154 } 155 156 if err == io.EOF { 157 result = buffer[i%-nth] 158 break 159 } 160 } else { 161 return "", err 162 } 163 } 164 } 165 166 return result, nil 167 } 168 169 // Do the plugin 170 func Do() { 171 var mtas []string 172 for k := range mailqFormats { 173 mtas = append(mtas, k) 174 } 175 176 mta := flag.String("mta", "", fmt.Sprintf("type of MTA (one of %v)", mtas)) 177 flag.StringVar(mta, "M", "", "shorthand for -mta") 178 command := flag.String("command", "", "path to queue-printing command (guessed by -M flag if not given)") 179 flag.StringVar(command, "c", "", "shorthand for -command") 180 tempfile := flag.String("tempfile", "", "path to tempfile") 181 keyPrefix := flag.String("metric-key-prefix", "mailq", "prefix to metric key") 182 labelPrefix := flag.String("metric-label-prefix", "Mailq", "prefix to metric label") 183 184 flag.Parse() 185 186 if format, ok := mailqFormats[*mta]; *mta == "" || !ok { 187 fmt.Fprintf(os.Stderr, "Unknown MTA: %s\n", *mta) 188 flag.PrintDefaults() 189 os.Exit(1) 190 } else { 191 plugin := &plugin{ 192 path: *command, 193 mailq: format, 194 keyPrefix: *keyPrefix, 195 labelPrefix: *labelPrefix, 196 } 197 helper := mp.NewMackerelPlugin(plugin) 198 helper.Tempfile = *tempfile 199 helper.Run() 200 } 201 }