diff --git a/go.mod b/go.mod index f507f761e..e6fbc78a5 100644 --- a/go.mod +++ b/go.mod @@ -29,13 +29,13 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mrobinsn/go-rtorrent v1.8.0 github.com/nxadm/tail v1.4.11 - github.com/shirou/gopsutil/v4 v4.24.6-0.20240625095728-865b8c3f58d6 + github.com/shirou/gopsutil/v4 v4.24.6 github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace github.com/stretchr/testify v1.9.0 github.com/swaggo/swag v1.16.3 - golang.org/x/crypto v0.24.0 + golang.org/x/crypto v0.25.0 golang.org/x/mod v0.19.0 - golang.org/x/sys v0.21.0 + golang.org/x/sys v0.22.0 golang.org/x/text v0.16.0 golang.org/x/time v0.5.0 golift.io/cache v0.0.2 @@ -104,15 +104,15 @@ require ( github.com/ulikunitz/xz v0.5.12 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.23.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.1 // indirect modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect - modernc.org/libc v1.53.4 // indirect + modernc.org/libc v1.54.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect diff --git a/go.sum b/go.sum index 7ad2e3c90..087294b30 100644 --- a/go.sum +++ b/go.sum @@ -228,8 +228,8 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/shirou/gopsutil/v4 v4.24.6-0.20240625095728-865b8c3f58d6 h1:onLMu4GbkyGnQVOCJS0Jl0MftM/22vqdBzm/f1TZxEo= -github.com/shirou/gopsutil/v4 v4.24.6-0.20240625095728-865b8c3f58d6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= +github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64= +github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -280,8 +280,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -309,8 +309,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -331,8 +329,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -374,8 +372,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -418,8 +416,8 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -428,8 +426,6 @@ golift.io/cache v0.0.2 h1:759rPK+EOy6UX7HkqsesOToSQ3CWN4UN3b5BiehfpeY= golift.io/cache v0.0.2/go.mod h1:wT61rGyiP50Rg243x5UF8dWBEKSr1RpB0GpgIF5kOGE= golift.io/cnfg v0.2.3 h1:cQsC4JS20njJyu5drtGefNmgN7M4HrLaRDNBPLit3pQ= golift.io/cnfg v0.2.3/go.mod h1:T4t8MFa8aZilCdIk1qQrN4mOGaFVPZ/qHQBBMbCIZJ0= -golift.io/cnfgfile v0.0.0-20230531075023-f880041cc0a0 h1:u14MTHfTGx7kLX2b3wFbaESrIm3YqL//HM92XA6wzH4= -golift.io/cnfgfile v0.0.0-20230531075023-f880041cc0a0/go.mod h1:dVC3N+72/nHNlBv3cwXb4FQTxMAor+JlNClAldwACkY= golift.io/cnfgfile v0.0.0-20240704165116-48378d0c6c38 h1:euXQUUWtsi2M+Bf6Do4+yG3YrVj88WyN0WJdv/abeW0= golift.io/cnfgfile v0.0.0-20240704165116-48378d0c6c38/go.mod h1:zHm9o8SkZ6Mm5DfGahsrEJPsogyR0qItP59s5lJ98/I= golift.io/datacounter v1.0.4 h1:b2gLQCs8WYRtKjOMG0qM82rF1o0+oKUXB9QH7kjmxZ4= @@ -504,18 +500,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -modernc.org/cc/v4 v4.21.3 h1:2mhBdWKtivdFlLR1ecKXTljPG1mfvbByX7QKztAIJl8= -modernc.org/cc/v4 v4.21.3/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.18.2 h1:PUQPShG4HwghpOekNujL0sFavdkRvmxzTbI4rGJ5mg0= -modernc.org/ccgo/v4 v4.18.2/go.mod h1:ao1fAxf9a2KEOL15WY8+yP3wnpaOpP/QuyFOZ9HJolM= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.19.0 h1:f9K5VdC0nVhHKTFMvhjtZ8TbRgFQbASvE5yO1zs8eC0= +modernc.org/ccgo/v4 v4.19.0/go.mod h1:CfpAl+673iXNwMG/aqcQn+vDcu4Es/YLya7+9RHjTa4= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8= modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.53.4 h1:YAgFS7tGIFBfqje2UOqiXtIwuDUCF8AUonYw0seup34= -modernc.org/libc v1.53.4/go.mod h1:aGsLofnkcct8lTJnKQnCqJO37ERAXSHamSuWLFoF2Cw= +modernc.org/libc v1.54.3 h1:pcPjjPJUHBRIJka4/R6AJ9FvxxuGRy0097QPjSMOdJs= +modernc.org/libc v1.54.3/go.mod h1:s3b2r/5Fre7gAEhWiMf+X1czfZ9KYx2rz4BBsnUf16E= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= diff --git a/pkg/client/instance_tests.go b/pkg/client/instance_tests.go index 3bddd0efa..be9400f6d 100644 --- a/pkg/client/instance_tests.go +++ b/pkg/client/instance_tests.go @@ -3,6 +3,7 @@ package client import ( "context" "fmt" + "html" "net/http" "net/url" "os" @@ -143,7 +144,7 @@ func (c *Client) testInstance(response http.ResponseWriter, request *http.Reques reply, code = testTautulli(request.Context(), config.Apps.Tautulli) } - http.Error(response, reply, code) + http.Error(response, html.EscapeString(reply), code) } func testDeluge(ctx context.Context, config *deluge.Config) (string, int) { @@ -342,10 +343,10 @@ func testTCP(ctx context.Context, svc *services.Service) (string, int) { res := svc.CheckOnly(ctx) if res.State != services.StateOK { - return res.State.String() + " " + res.Output, http.StatusBadGateway + return res.State.String() + " " + res.Output.String(), http.StatusBadGateway } - return "TCP Port is OPEN and reachable: " + res.Output, http.StatusOK + return "TCP Port is OPEN and reachable: " + res.Output.String(), http.StatusOK } func testHTTP(ctx context.Context, svc *services.Service) (string, int) { @@ -355,11 +356,11 @@ func testHTTP(ctx context.Context, svc *services.Service) (string, int) { res := svc.CheckOnly(ctx) if res.State != services.StateOK { - return res.State.String() + " " + res.Output, http.StatusBadGateway + return res.State.String() + " " + res.Output.String(), http.StatusBadGateway } // add test - return "HTTP Response Code Acceptable! " + res.Output, http.StatusOK + return "HTTP Response Code Acceptable! " + res.Output.String(), http.StatusOK } func testProcess(ctx context.Context, svc *services.Service) (string, int) { @@ -369,10 +370,10 @@ func testProcess(ctx context.Context, svc *services.Service) (string, int) { res := svc.CheckOnly(ctx) if res.State != services.StateOK { - return res.State.String() + " " + res.Output, http.StatusBadGateway + return res.State.String() + " " + res.Output.String(), http.StatusBadGateway } - return "Process Tested OK: " + res.Output, http.StatusOK + return "Process Tested OK: " + res.Output.String(), http.StatusOK } func testPing(ctx context.Context, svc *services.Service) (string, int) { @@ -382,10 +383,10 @@ func testPing(ctx context.Context, svc *services.Service) (string, int) { res := svc.CheckOnly(ctx) if res.State != services.StateOK { - return res.State.String() + " " + res.Output, http.StatusBadGateway + return res.State.String() + " " + res.Output.String(), http.StatusBadGateway } - return "Ping Tested OK: " + res.Output, http.StatusOK + return "Ping Tested OK: " + res.Output.String(), http.StatusOK } func testPlex(ctx context.Context, app *apps.PlexConfig) (string, int) { diff --git a/pkg/services/check_ping.go b/pkg/services/check_ping.go index 9a28a4e94..28bca85c6 100644 --- a/pkg/services/check_ping.go +++ b/pkg/services/check_ping.go @@ -77,7 +77,7 @@ func (s *Service) checkPING() *result { if err != nil { return &result{ state: StateUnknown, - output: "invalid ping value: " + err.Error(), + output: &Output{str: "invalid ping value: " + err.Error()}, } } @@ -89,7 +89,7 @@ func (s *Service) checkPING() *result { if err = pinger.Run(); err != nil { // blocks. return &result{ state: StateCritical, - output: "error pinging service: " + err.Error(), + output: &Output{str: "error pinging service: " + err.Error()}, } } @@ -105,8 +105,8 @@ func (s *Service) checkPING() *result { return &result{ state: state, - output: fmt.Sprintf("(%s) pkts sent:%d, rcvd:%d, loss:%.01f, max:%s, avg:%s", + output: &Output{str: fmt.Sprintf("(%s) pkts sent:%d, rcvd:%d, loss:%.01f, max:%s, avg:%s", msg, stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss, - stats.MaxRtt.Round(time.Millisecond), stats.AvgRtt.Round(time.Millisecond)), + stats.MaxRtt.Round(time.Millisecond), stats.AvgRtt.Round(time.Millisecond))}, } } diff --git a/pkg/services/check_proc.go b/pkg/services/check_proc.go index 409df9e0b..d1038f17d 100644 --- a/pkg/services/check_proc.go +++ b/pkg/services/check_proc.go @@ -74,7 +74,7 @@ func (s *Service) checkProccess(ctx context.Context) *result { if err != nil { return &result{ state: StateUnknown, - output: "process list error: " + err.Error(), + output: &Output{str: "process list error: " + err.Error()}, } } @@ -106,8 +106,8 @@ func (s *Service) getProcessResults(ctx context.Context, processes []*process.Pr if !procinfo.Created.IsZero() && s.svc.proc.restarts && time.Since(procinfo.Created) < s.Interval.Duration { return &result{ state: StateCritical, - output: fmt.Sprintf("%s: process restarted since last check, age: %v, pid: %d, proc: %s", - s.Value, time.Since(procinfo.Created), proc.Pid, procinfo.CmdLine), + output: &Output{str: fmt.Sprintf("%s: process restarted since last check, age: %v, pid: %d, proc: %s", + s.Value, time.Since(procinfo.Created), proc.Pid, procinfo.CmdLine)}, } } } @@ -128,17 +128,17 @@ func (s *Service) checkProcessCounts(pids []int32, ages []time.Time) *result { case count < s.svc.proc.countMin: // not enough running! return &result{ state: StateCritical, - output: fmt.Sprintf("%s: found %d processes; %s%s%s%s", s.Value, count, min, max, age, pid), + output: &Output{str: fmt.Sprintf("%s: found %d processes; %s%s%s%s", s.Value, count, min, max, age, pid)}, } case s.svc.proc.running && count > 0: // running but should not be! return &result{ state: StateCritical, - output: fmt.Sprintf("%s: found %d processes; expected: 0%s%s", s.Value, count, age, pid), + output: &Output{str: fmt.Sprintf("%s: found %d processes; expected: 0%s%s", s.Value, count, age, pid)}, } default: // running within thresholds! return &result{ state: StateOK, - output: fmt.Sprintf("%s: found %d processes; %s%s%s%s", s.Value, count, min, max, age, pid), + output: &Output{str: fmt.Sprintf("%s: found %d processes; %s%s%s%s", s.Value, count, min, max, age, pid)}, } } } diff --git a/pkg/services/checks.go b/pkg/services/checks.go index fde81c969..a1f6f651d 100644 --- a/pkg/services/checks.go +++ b/pkg/services/checks.go @@ -24,7 +24,7 @@ const ( ) type result struct { - output string + output *Output state CheckState } @@ -185,7 +185,7 @@ func (s *Service) checkHTTPReq(ctx context.Context) (*http.Client, *http.Request func (s *Service) checkHTTP(ctx context.Context) *result { res := &result{ state: StateUnknown, - output: "unknown", + output: &Output{str: "unknown"}, } ctx, cancel := context.WithTimeout(ctx, s.Timeout.Duration) @@ -193,7 +193,7 @@ func (s *Service) checkHTTP(ctx context.Context) *result { client, req, err := s.checkHTTPReq(ctx) if err != nil { - res.output = "creating request: " + RemoveSecrets(s.Value, err.Error()) + res.output = &Output{str: "creating request: " + RemoveSecrets(s.Value, err.Error())} return res } @@ -202,39 +202,39 @@ func (s *Service) checkHTTP(ctx context.Context) *result { resp, err := client.Do(req) if err != nil { - res.output = "making request: " + RemoveSecrets(s.Value, err.Error()) + res.output = &Output{str: "making request: " + RemoveSecrets(s.Value, err.Error())} return res } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - res.output = "reading body: " + RemoveSecrets(s.Value, err.Error()) + res.output = &Output{str: "reading body: " + RemoveSecrets(s.Value, err.Error())} return res } for _, code := range strings.Split(s.Expect, expectdelim) { if strconv.Itoa(resp.StatusCode) == strings.TrimSpace(code) { res.state = StateOK - res.output = resp.Status + res.output = &Output{str: resp.Status} return res } } // Reduce the size of the string before processing it to speed things up on large body outputs. - if len(res.output) > maxOutput+maxOutput { - res.output = res.output[:maxOutput+maxOutput] + if len(res.output.str) > maxOutput+maxOutput { + res.output.str = res.output.str[:maxOutput+maxOutput] } res.state = StateCritical - res.output = resp.Status + ": " + strings.TrimSpace( - html.EscapeString(strings.Join(strings.Fields(RemoveSecrets(s.Value, string(body))), " "))) + res.output = &Output{esc: true, str: resp.Status + ": " + strings.TrimSpace( + html.EscapeString(strings.Join(strings.Fields(RemoveSecrets(s.Value, string(body))), " ")))} // Reduce the string to the final max length. // We do it this way so all secrets are properly escaped before string splitting. - if len(res.output) > maxOutput { - res.output = res.output[:maxOutput] + if len(res.output.str) > maxOutput { + res.output.str = res.output.str[:maxOutput] } return res @@ -259,21 +259,21 @@ func RemoveSecrets(appURL, message string) string { func (s *Service) checkTCP() *result { res := &result{ state: StateUnknown, - output: "unknown", + output: &Output{str: "unknown"}, } switch conn, err := net.DialTimeout("tcp", s.Value, s.Timeout.Duration); { case err != nil: res.state = StateCritical - res.output = "connection error: " + err.Error() + res.output = &Output{str: "connection error: " + err.Error()} case conn == nil: res.state = StateUnknown - res.output = "connection failed, no specific error" + res.output = &Output{str: "connection failed, no specific error"} default: conn.Close() res.state = StateOK - res.output = "connected to port " + strings.Split(s.Value, ":")[1] + " OK" + res.output = &Output{str: "connected to port " + strings.Split(s.Value, ":")[1] + " OK"} } return res diff --git a/pkg/services/config.go b/pkg/services/config.go index a4a2f965c..2f3ba0d0f 100644 --- a/pkg/services/config.go +++ b/pkg/services/config.go @@ -1,7 +1,9 @@ package services import ( + "encoding/json" "fmt" + "html" "sync" "time" @@ -87,7 +89,7 @@ type Results struct { type CheckResult struct { Name string `json:"name"` // "Radarr" State CheckState `json:"state"` // 0 = OK, 1 = Warn, 2 = Crit, 3 = Unknown - Output string `json:"output"` // metadata message + Output *Output `json:"output"` // metadata message must never be nil. Type CheckType `json:"type"` // http, tcp, ping Time time.Time `json:"time"` // when it was checked, rounded to Microseconds Since time.Time `json:"since"` // how long it has been in this state, rounded to Microseconds @@ -112,7 +114,7 @@ type Service struct { } type service struct { - Output string `json:"output"` + Output *Output `json:"output"` State CheckState `json:"state"` Since time.Time `json:"since"` LastCheck time.Time `json:"lastCheck"` @@ -121,3 +123,23 @@ type service struct { ping *pingExpect // only used for icmp/udp ping checks. sync.RWMutex `json:"-"` } + +type Output struct { + str string // output string + esc bool // html escaped? +} + +func (o *Output) String() string { + switch { + case o == nil: + return "" + case o.esc: + return html.UnescapeString(o.str) + default: + return o.str + } +} + +func (o *Output) MarshalJSON() ([]byte, error) { + return json.Marshal(o.str) //nolint:wrapcheck // do not unescape it. +}