github.com/netdata/go.d.plugin@v0.58.1/modules/supervisord/client.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package supervisord 4 5 import ( 6 "context" 7 "errors" 8 "fmt" 9 "net" 10 "net/http" 11 "net/url" 12 "strings" 13 14 "github.com/mattn/go-xmlrpc" 15 ) 16 17 type supervisorRPCClient struct { 18 client *xmlrpc.Client 19 } 20 21 func newSupervisorRPCClient(serverURL *url.URL, httpClient *http.Client) (supervisorClient, error) { 22 switch serverURL.Scheme { 23 case "http", "https": 24 c := xmlrpc.NewClient(serverURL.String()) 25 c.HttpClient = httpClient 26 return &supervisorRPCClient{client: c}, nil 27 case "unix": 28 c := xmlrpc.NewClient("http://unix/RPC2") 29 t, ok := httpClient.Transport.(*http.Transport) 30 if !ok { 31 return nil, errors.New("unexpected HTTP client transport") 32 } 33 t.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { 34 d := net.Dialer{Timeout: httpClient.Timeout} 35 return d.DialContext(ctx, "unix", serverURL.Path) 36 } 37 c.HttpClient = httpClient 38 return &supervisorRPCClient{client: c}, nil 39 default: 40 return nil, fmt.Errorf("unexpected URL scheme: %s", serverURL) 41 } 42 } 43 44 // http://supervisord.org/api.html#process-control 45 type processStatus struct { 46 name string // name of the process. 47 group string // name of the process’ group. 48 start int // UNIX timestamp of when the process was started. 49 stop int // UNIX timestamp of when the process last ended, or 0 if the process has never been stopped. 50 now int // UNIX timestamp of the current time, which can be used to calculate process up-time. 51 state int // state code. 52 stateName string // string description of state. 53 exitStatus int // exit status (errorlevel) of process, or 0 if the process is still running. 54 } 55 56 func (c *supervisorRPCClient) getAllProcessInfo() ([]processStatus, error) { 57 const fn = "supervisor.getAllProcessInfo" 58 resp, err := c.client.Call(fn) 59 if err != nil { 60 return nil, fmt.Errorf("error on '%s' function call: %v", fn, err) 61 } 62 return parseGetAllProcessInfo(resp) 63 } 64 65 func (c *supervisorRPCClient) closeIdleConnections() { 66 c.client.HttpClient.CloseIdleConnections() 67 } 68 69 func parseGetAllProcessInfo(resp interface{}) ([]processStatus, error) { 70 arr, ok := resp.(xmlrpc.Array) 71 if !ok { 72 return nil, fmt.Errorf("unexpected response type, want=xmlrpc.Array, got=%T", resp) 73 } 74 75 var info []processStatus 76 77 for _, item := range arr { 78 s, ok := item.(xmlrpc.Struct) 79 if !ok { 80 continue 81 } 82 83 var p processStatus 84 for k, v := range s { 85 switch strings.ToLower(k) { 86 case "name": 87 p.name, _ = v.(string) 88 case "group": 89 p.group, _ = v.(string) 90 case "start": 91 p.start, _ = v.(int) 92 case "stop": 93 p.stop, _ = v.(int) 94 case "now": 95 p.now, _ = v.(int) 96 case "state": 97 p.state, _ = v.(int) 98 case "statename": 99 p.stateName, _ = v.(string) 100 case "exitstatus": 101 p.exitStatus, _ = v.(int) 102 } 103 } 104 if p.name != "" && p.group != "" && p.stateName != "" { 105 info = append(info, p) 106 } 107 } 108 return info, nil 109 }