84a4f9b6e
[haproxy-3.0.git] /
1 /*
2  * Functions dedicated to statistics output and the stats socket
3  *
4  * Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
5  * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version
10  * 2 of the License, or (at your option) any later version.
11  *
12  */
13
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <pwd.h>
20 #include <grp.h>
21
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #include <haproxy/api.h>
27 #include <haproxy/activity.h>
28 #include <haproxy/applet.h>
29 #include <haproxy/backend.h>
30 #include <haproxy/base64.h>
31 #include <haproxy/cfgparse.h>
32 #include <haproxy/channel.h>
33 #include <haproxy/check.h>
34 #include <haproxy/cli.h>
35 #include <haproxy/clock.h>
36 #include <haproxy/compression.h>
37 #include <haproxy/debug.h>
38 #include <haproxy/errors.h>
39 #include <haproxy/fd.h>
40 #include <haproxy/freq_ctr.h>
41 #include <haproxy/frontend.h>
42 #include <haproxy/global.h>
43 #include <haproxy/http.h>
44 #include <haproxy/http_ana.h>
45 #include <haproxy/http_htx.h>
46 #include <haproxy/htx.h>
47 #include <haproxy/list.h>
48 #include <haproxy/listener.h>
49 #include <haproxy/log.h>
50 #include <haproxy/map-t.h>
51 #include <haproxy/pattern-t.h>
52 #include <haproxy/pipe.h>
53 #include <haproxy/pool.h>
54 #include <haproxy/proxy.h>
55 #include <haproxy/resolvers.h>
56 #include <haproxy/sc_strm.h>
57 #include <haproxy/server.h>
58 #include <haproxy/session.h>
59 #include <haproxy/stats.h>
60 #include <haproxy/stconn.h>
61 #include <haproxy/stream.h>
62 #include <haproxy/task.h>
63 #include <haproxy/ticks.h>
64 #include <haproxy/time.h>
65 #include <haproxy/tools.h>
66 #include <haproxy/uri_auth-t.h>
67 #include <haproxy/version.h>
68
69
70 /* status codes available for the stats admin page (strictly 4 chars length) */
71 const char *stat_status_codes[STAT_STATUS_SIZE] = {
72         [STAT_STATUS_DENY] = "DENY",
73         [STAT_STATUS_DONE] = "DONE",
74         [STAT_STATUS_ERRP] = "ERRP",
75         [STAT_STATUS_EXCD] = "EXCD",
76         [STAT_STATUS_NONE] = "NONE",
77         [STAT_STATUS_PART] = "PART",
78         [STAT_STATUS_UNKN] = "UNKN",
79         [STAT_STATUS_IVAL] = "IVAL",
80 };
81
82 /* These are the field names for each INF_* field position. Please pay attention
83  * to always use the exact same name except that the strings for new names must
84  * be lower case or CamelCase while the enum entries must be upper case.
85  */
86 const struct name_desc info_fields[INF_TOTAL_FIELDS] = {
87         [INF_NAME]                           = { .name = "Name",                        .desc = "Product name" },
88         [INF_VERSION]                        = { .name = "Version",                     .desc = "Product version" },
89         [INF_RELEASE_DATE]                   = { .name = "Release_date",                .desc = "Date of latest source code update" },
90         [INF_NBTHREAD]                       = { .name = "Nbthread",                    .desc = "Number of started threads (global.nbthread)" },
91         [INF_NBPROC]                         = { .name = "Nbproc",                      .desc = "Number of started worker processes (historical, always 1)" },
92         [INF_PROCESS_NUM]                    = { .name = "Process_num",                 .desc = "Relative worker process number (1)" },
93         [INF_PID]                            = { .name = "Pid",                         .desc = "This worker process identifier for the system" },
94         [INF_UPTIME]                         = { .name = "Uptime",                      .desc = "How long ago this worker process was started (days+hours+minutes+seconds)" },
95         [INF_UPTIME_SEC]                     = { .name = "Uptime_sec",                  .desc = "How long ago this worker process was started (seconds)" },
96         [INF_START_TIME_SEC]                 = { .name = "Start_time_sec",              .desc = "Start time in seconds" },
97         [INF_MEMMAX_MB]                      = { .name = "Memmax_MB",                   .desc = "Worker process's hard limit on memory usage in MB (-m on command line)" },
98         [INF_MEMMAX_BYTES]                   = { .name = "Memmax_bytes",                .desc = "Worker process's hard limit on memory usage in byes (-m on command line)" },
99         [INF_POOL_ALLOC_MB]                  = { .name = "PoolAlloc_MB",                .desc = "Amount of memory allocated in pools (in MB)" },
100         [INF_POOL_ALLOC_BYTES]               = { .name = "PoolAlloc_bytes",             .desc = "Amount of memory allocated in pools (in bytes)" },
101         [INF_POOL_USED_MB]                   = { .name = "PoolUsed_MB",                 .desc = "Amount of pool memory currently used (in MB)" },
102         [INF_POOL_USED_BYTES]                = { .name = "PoolUsed_bytes",              .desc = "Amount of pool memory currently used (in bytes)" },
103         [INF_POOL_FAILED]                    = { .name = "PoolFailed",                  .desc = "Number of failed pool allocations since this worker was started" },
104         [INF_ULIMIT_N]                       = { .name = "Ulimit-n",                    .desc = "Hard limit on the number of per-process file descriptors" },
105         [INF_MAXSOCK]                        = { .name = "Maxsock",                     .desc = "Hard limit on the number of per-process sockets" },
106         [INF_MAXCONN]                        = { .name = "Maxconn",                     .desc = "Hard limit on the number of per-process connections (configured or imposed by Ulimit-n)" },
107         [INF_HARD_MAXCONN]                   = { .name = "Hard_maxconn",                .desc = "Hard limit on the number of per-process connections (imposed by Memmax_MB or Ulimit-n)" },
108         [INF_CURR_CONN]                      = { .name = "CurrConns",                   .desc = "Current number of connections on this worker process" },
109         [INF_CUM_CONN]                       = { .name = "CumConns",                    .desc = "Total number of connections on this worker process since started" },
110         [INF_CUM_REQ]                        = { .name = "CumReq",                      .desc = "Total number of requests on this worker process since started" },
111         [INF_MAX_SSL_CONNS]                  = { .name = "MaxSslConns",                 .desc = "Hard limit on the number of per-process SSL endpoints (front+back), 0=unlimited" },
112         [INF_CURR_SSL_CONNS]                 = { .name = "CurrSslConns",                .desc = "Current number of SSL endpoints on this worker process (front+back)" },
113         [INF_CUM_SSL_CONNS]                  = { .name = "CumSslConns",                 .desc = "Total number of SSL endpoints on this worker process since started (front+back)" },
114         [INF_MAXPIPES]                       = { .name = "Maxpipes",                    .desc = "Hard limit on the number of pipes for splicing, 0=unlimited" },
115         [INF_PIPES_USED]                     = { .name = "PipesUsed",                   .desc = "Current number of pipes in use in this worker process" },
116         [INF_PIPES_FREE]                     = { .name = "PipesFree",                   .desc = "Current number of allocated and available pipes in this worker process" },
117         [INF_CONN_RATE]                      = { .name = "ConnRate",                    .desc = "Number of front connections created on this worker process over the last second" },
118         [INF_CONN_RATE_LIMIT]                = { .name = "ConnRateLimit",               .desc = "Hard limit for ConnRate (global.maxconnrate)" },
119         [INF_MAX_CONN_RATE]                  = { .name = "MaxConnRate",                 .desc = "Highest ConnRate reached on this worker process since started (in connections per second)" },
120         [INF_SESS_RATE]                      = { .name = "SessRate",                    .desc = "Number of sessions created on this worker process over the last second" },
121         [INF_SESS_RATE_LIMIT]                = { .name = "SessRateLimit",               .desc = "Hard limit for SessRate (global.maxsessrate)" },
122         [INF_MAX_SESS_RATE]                  = { .name = "MaxSessRate",                 .desc = "Highest SessRate reached on this worker process since started (in sessions per second)" },
123         [INF_SSL_RATE]                       = { .name = "SslRate",                     .desc = "Number of SSL connections created on this worker process over the last second" },
124         [INF_SSL_RATE_LIMIT]                 = { .name = "SslRateLimit",                .desc = "Hard limit for SslRate (global.maxsslrate)" },
125         [INF_MAX_SSL_RATE]                   = { .name = "MaxSslRate",                  .desc = "Highest SslRate reached on this worker process since started (in connections per second)" },
126         [INF_SSL_FRONTEND_KEY_RATE]          = { .name = "SslFrontendKeyRate",          .desc = "Number of SSL keys created on frontends in this worker process over the last second" },
127         [INF_SSL_FRONTEND_MAX_KEY_RATE]      = { .name = "SslFrontendMaxKeyRate",       .desc = "Highest SslFrontendKeyRate reached on this worker process since started (in SSL keys per second)" },
128         [INF_SSL_FRONTEND_SESSION_REUSE_PCT] = { .name = "SslFrontendSessionReuse_pct", .desc = "Percent of frontend SSL connections which did not require a new key" },
129         [INF_SSL_BACKEND_KEY_RATE]           = { .name = "SslBackendKeyRate",           .desc = "Number of SSL keys created on backends in this worker process over the last second" },
130         [INF_SSL_BACKEND_MAX_KEY_RATE]       = { .name = "SslBackendMaxKeyRate",        .desc = "Highest SslBackendKeyRate reached on this worker process since started (in SSL keys per second)" },
131         [INF_SSL_CACHE_LOOKUPS]              = { .name = "SslCacheLookups",             .desc = "Total number of SSL session ID lookups in the SSL session cache on this worker since started" },
132         [INF_SSL_CACHE_MISSES]               = { .name = "SslCacheMisses",              .desc = "Total number of SSL session ID lookups that didn't find a session in the SSL session cache on this worker since started" },
133         [INF_COMPRESS_BPS_IN]                = { .name = "CompressBpsIn",               .desc = "Number of bytes submitted to the HTTP compressor in this worker process over the last second" },
134         [INF_COMPRESS_BPS_OUT]               = { .name = "CompressBpsOut",              .desc = "Number of bytes emitted by the HTTP compressor in this worker process over the last second" },
135         [INF_COMPRESS_BPS_RATE_LIM]          = { .name = "CompressBpsRateLim",          .desc = "Limit of CompressBpsOut beyond which HTTP compression is automatically disabled" },
136         [INF_ZLIB_MEM_USAGE]                 = { .name = "ZlibMemUsage",                .desc = "Amount of memory currently used by HTTP compression on the current worker process (in bytes)" },
137         [INF_MAX_ZLIB_MEM_USAGE]             = { .name = "MaxZlibMemUsage",             .desc = "Limit on the amount of memory used by HTTP compression above which it is automatically disabled (in bytes, see global.maxzlibmem)" },
138         [INF_TASKS]                          = { .name = "Tasks",                       .desc = "Total number of tasks in the current worker process (active + sleeping)" },
139         [INF_RUN_QUEUE]                      = { .name = "Run_queue",                   .desc = "Total number of active tasks+tasklets in the current worker process" },
140         [INF_IDLE_PCT]                       = { .name = "Idle_pct",                    .desc = "Percentage of last second spent waiting in the current worker thread" },
141         [INF_NODE]                           = { .name = "node",                        .desc = "Node name (global.node)" },
142         [INF_DESCRIPTION]                    = { .name = "description",                 .desc = "Node description (global.description)" },
143         [INF_STOPPING]                       = { .name = "Stopping",                    .desc = "1 if the worker process is currently stopping, otherwise zero" },
144         [INF_JOBS]                           = { .name = "Jobs",                        .desc = "Current number of active jobs on the current worker process (frontend connections, master connections, listeners)" },
145         [INF_UNSTOPPABLE_JOBS]               = { .name = "Unstoppable Jobs",            .desc = "Current number of unstoppable jobs on the current worker process (master connections)" },
146         [INF_LISTENERS]                      = { .name = "Listeners",                   .desc = "Current number of active listeners on the current worker process" },
147         [INF_ACTIVE_PEERS]                   = { .name = "ActivePeers",                 .desc = "Current number of verified active peers connections on the current worker process" },
148         [INF_CONNECTED_PEERS]                = { .name = "ConnectedPeers",              .desc = "Current number of peers having passed the connection step on the current worker process" },
149         [INF_DROPPED_LOGS]                   = { .name = "DroppedLogs",                 .desc = "Total number of dropped logs for current worker process since started" },
150         [INF_BUSY_POLLING]                   = { .name = "BusyPolling",                 .desc = "1 if busy-polling is currently in use on the worker process, otherwise zero (config.busy-polling)" },
151         [INF_FAILED_RESOLUTIONS]             = { .name = "FailedResolutions",           .desc = "Total number of failed DNS resolutions in current worker process since started" },
152         [INF_TOTAL_BYTES_OUT]                = { .name = "TotalBytesOut",               .desc = "Total number of bytes emitted by current worker process since started" },
153         [INF_TOTAL_SPLICED_BYTES_OUT]        = { .name = "TotalSplicdedBytesOut",       .desc = "Total number of bytes emitted by current worker process through a kernel pipe since started" },
154         [INF_BYTES_OUT_RATE]                 = { .name = "BytesOutRate",                .desc = "Number of bytes emitted by current worker process over the last second" },
155         [INF_DEBUG_COMMANDS_ISSUED]          = { .name = "DebugCommandsIssued",         .desc = "Number of debug commands issued on this process (anything > 0 is unsafe)" },
156         [INF_CUM_LOG_MSGS]                   = { .name = "CumRecvLogs",                 .desc = "Total number of log messages received by log-forwarding listeners on this worker process since started" },
157         [INF_BUILD_INFO]                     = { .name = "Build info",                  .desc = "Build info" },
158         [INF_TAINTED]                        = { .name = "Tainted",                     .desc = "Experimental features used" },
159 };
160
161 const struct name_desc stat_fields[ST_F_TOTAL_FIELDS] = {
162         [ST_F_PXNAME]                        = { .name = "pxname",                      .desc = "Proxy name" },
163         [ST_F_SVNAME]                        = { .name = "svname",                      .desc = "Server name" },
164         [ST_F_QCUR]                          = { .name = "qcur",                        .desc = "Number of current queued connections" },
165         [ST_F_QMAX]                          = { .name = "qmax",                        .desc = "Highest value of queued connections encountered since process started" },
166         [ST_F_SCUR]                          = { .name = "scur",                        .desc = "Number of current sessions on the frontend, backend or server" },
167         [ST_F_SMAX]                          = { .name = "smax",                        .desc = "Highest value of current sessions encountered since process started" },
168         [ST_F_SLIM]                          = { .name = "slim",                        .desc = "Frontend/listener/server's maxconn, backend's fullconn" },
169         [ST_F_STOT]                          = { .name = "stot",                        .desc = "Total number of sessions since process started" },
170         [ST_F_BIN]                           = { .name = "bin",                         .desc = "Total number of request bytes since process started" },
171         [ST_F_BOUT]                          = { .name = "bout",                        .desc = "Total number of response bytes since process started" },
172         [ST_F_DREQ]                          = { .name = "dreq",                        .desc = "Total number of denied requests since process started" },
173         [ST_F_DRESP]                         = { .name = "dresp",                       .desc = "Total number of denied responses since process started" },
174         [ST_F_EREQ]                          = { .name = "ereq",                        .desc = "Total number of invalid requests since process started" },
175         [ST_F_ECON]                          = { .name = "econ",                        .desc = "Total number of failed connections to server since the worker process started" },
176         [ST_F_ERESP]                         = { .name = "eresp",                       .desc = "Total number of invalid responses since the worker process started" },
177         [ST_F_WRETR]                         = { .name = "wretr",                       .desc = "Total number of server connection retries since the worker process started" },
178         [ST_F_WREDIS]                        = { .name = "wredis",                      .desc = "Total number of server redispatches due to connection failures since the worker process started" },
179         [ST_F_STATUS]                        = { .name = "status",                      .desc = "Frontend/listen status: OPEN/WAITING/FULL/STOP; backend: UP/DOWN; server: last check status" },
180         [ST_F_WEIGHT]                        = { .name = "weight",                      .desc = "Server's effective weight, or sum of active servers' effective weights for a backend" },
181         [ST_F_ACT]                           = { .name = "act",                         .desc = "Total number of active UP servers with a non-zero weight" },
182         [ST_F_BCK]                           = { .name = "bck",                         .desc = "Total number of backup UP servers with a non-zero weight" },
183         [ST_F_CHKFAIL]                       = { .name = "chkfail",                     .desc = "Total number of failed individual health checks per server/backend, since the worker process started" },
184         [ST_F_CHKDOWN]                       = { .name = "chkdown",                     .desc = "Total number of failed checks causing UP to DOWN server transitions, per server/backend, since the worker process started" },
185         [ST_F_LASTCHG]                       = { .name = "lastchg",                     .desc = "How long ago the last server state changed, in seconds" },
186         [ST_F_DOWNTIME]                      = { .name = "downtime",                    .desc = "Total time spent in DOWN state, for server or backend" },
187         [ST_F_QLIMIT]                        = { .name = "qlimit",                      .desc = "Limit on the number of connections in queue, for servers only (maxqueue argument)" },
188         [ST_F_PID]                           = { .name = "pid",                         .desc = "Relative worker process number (1)" },
189         [ST_F_IID]                           = { .name = "iid",                         .desc = "Frontend or Backend numeric identifier ('id' setting)" },
190         [ST_F_SID]                           = { .name = "sid",                         .desc = "Server numeric identifier ('id' setting)" },
191         [ST_F_THROTTLE]                      = { .name = "throttle",                    .desc = "Throttling ratio applied to a server's maxconn and weight during the slowstart period (0 to 100%)" },
192         [ST_F_LBTOT]                         = { .name = "lbtot",                       .desc = "Total number of requests routed by load balancing since the worker process started (ignores queue pop and stickiness)" },
193         [ST_F_TRACKED]                       = { .name = "tracked",                     .desc = "Name of the other server this server tracks for its state" },
194         [ST_F_TYPE]                          = { .name = "type",                        .desc = "Type of the object (Listener, Frontend, Backend, Server)" },
195         [ST_F_RATE]                          = { .name = "rate",                        .desc = "Total number of sessions processed by this object over the last second (sessions for listeners/frontends, requests for backends/servers)" },
196         [ST_F_RATE_LIM]                      = { .name = "rate_lim",                    .desc = "Limit on the number of sessions accepted in a second (frontend only, 'rate-limit sessions' setting)" },
197         [ST_F_RATE_MAX]                      = { .name = "rate_max",                    .desc = "Highest value of sessions per second observed since the worker process started" },
198         [ST_F_CHECK_STATUS]                  = { .name = "check_status",                .desc = "Status report of the server's latest health check, prefixed with '*' if a check is currently in progress" },
199         [ST_F_CHECK_CODE]                    = { .name = "check_code",                  .desc = "HTTP/SMTP/LDAP status code reported by the latest server health check" },
200         [ST_F_CHECK_DURATION]                = { .name = "check_duration",              .desc = "Total duration of the latest server health check, in milliseconds" },
201         [ST_F_HRSP_1XX]                      = { .name = "hrsp_1xx",                    .desc = "Total number of HTTP responses with status 100-199 returned by this object since the worker process started" },
202         [ST_F_HRSP_2XX]                      = { .name = "hrsp_2xx",                    .desc = "Total number of HTTP responses with status 200-299 returned by this object since the worker process started" },
203         [ST_F_HRSP_3XX]                      = { .name = "hrsp_3xx",                    .desc = "Total number of HTTP responses with status 300-399 returned by this object since the worker process started" },
204         [ST_F_HRSP_4XX]                      = { .name = "hrsp_4xx",                    .desc = "Total number of HTTP responses with status 400-499 returned by this object since the worker process started" },
205         [ST_F_HRSP_5XX]                      = { .name = "hrsp_5xx",                    .desc = "Total number of HTTP responses with status 500-599 returned by this object since the worker process started" },
206         [ST_F_HRSP_OTHER]                    = { .name = "hrsp_other",                  .desc = "Total number of HTTP responses with status <100, >599 returned by this object since the worker process started (error -1 included)" },
207         [ST_F_HANAFAIL]                      = { .name = "hanafail",                    .desc = "Total number of failed checks caused by an 'on-error' directive after an 'observe' condition matched" },
208         [ST_F_REQ_RATE]                      = { .name = "req_rate",                    .desc = "Number of HTTP requests processed over the last second on this object" },
209         [ST_F_REQ_RATE_MAX]                  = { .name = "req_rate_max",                .desc = "Highest value of http requests observed since the worker process started" },
210         [ST_F_REQ_TOT]                       = { .name = "req_tot",                     .desc = "Total number of HTTP requests processed by this object since the worker process started" },
211         [ST_F_CLI_ABRT]                      = { .name = "cli_abrt",                    .desc = "Total number of requests or connections aborted by the client since the worker process started" },
212         [ST_F_SRV_ABRT]                      = { .name = "srv_abrt",                    .desc = "Total number of requests or connections aborted by the server since the worker process started" },
213         [ST_F_COMP_IN]                       = { .name = "comp_in",                     .desc = "Total number of bytes submitted to the HTTP compressor for this object since the worker process started" },
214         [ST_F_COMP_OUT]                      = { .name = "comp_out",                    .desc = "Total number of bytes emitted by the HTTP compressor for this object since the worker process started" },
215         [ST_F_COMP_BYP]                      = { .name = "comp_byp",                    .desc = "Total number of bytes that bypassed HTTP compression for this object since the worker process started (CPU/memory/bandwidth limitation)" },
216         [ST_F_COMP_RSP]                      = { .name = "comp_rsp",                    .desc = "Total number of HTTP responses that were compressed for this object since the worker process started" },
217         [ST_F_LASTSESS]                      = { .name = "lastsess",                    .desc = "How long ago some traffic was seen on this object on this worker process, in seconds" },
218         [ST_F_LAST_CHK]                      = { .name = "last_chk",                    .desc = "Short description of the latest health check report for this server (see also check_desc)" },
219         [ST_F_LAST_AGT]                      = { .name = "last_agt",                    .desc = "Short description of the latest agent check report for this server (see also agent_desc)" },
220         [ST_F_QTIME]                         = { .name = "qtime",                       .desc = "Time spent in the queue, in milliseconds, averaged over the 1024 last requests (backend/server)" },
221         [ST_F_CTIME]                         = { .name = "ctime",                       .desc = "Time spent waiting for a connection to complete, in milliseconds, averaged over the 1024 last requests (backend/server)" },
222         [ST_F_RTIME]                         = { .name = "rtime",                       .desc = "Time spent waiting for a server response, in milliseconds, averaged over the 1024 last requests (backend/server)" },
223         [ST_F_TTIME]                         = { .name = "ttime",                       .desc = "Total request+response time (request+queue+connect+response+processing), in milliseconds, averaged over the 1024 last requests (backend/server)" },
224         [ST_F_AGENT_STATUS]                  = { .name = "agent_status",                .desc = "Status report of the server's latest agent check, prefixed with '*' if a check is currently in progress" },
225         [ST_F_AGENT_CODE]                    = { .name = "agent_code",                  .desc = "Status code reported by the latest server agent check" },
226         [ST_F_AGENT_DURATION]                = { .name = "agent_duration",              .desc = "Total duration of the latest server agent check, in milliseconds" },
227         [ST_F_CHECK_DESC]                    = { .name = "check_desc",                  .desc = "Textual description of the latest health check report for this server" },
228         [ST_F_AGENT_DESC]                    = { .name = "agent_desc",                  .desc = "Textual description of the latest agent check report for this server" },
229         [ST_F_CHECK_RISE]                    = { .name = "check_rise",                  .desc = "Number of successful health checks before declaring a server UP (server 'rise' setting)" },
230         [ST_F_CHECK_FALL]                    = { .name = "check_fall",                  .desc = "Number of failed health checks before declaring a server DOWN (server 'fall' setting)" },
231         [ST_F_CHECK_HEALTH]                  = { .name = "check_health",                .desc = "Current server health check level (0..fall-1=DOWN, fall..rise-1=UP)" },
232         [ST_F_AGENT_RISE]                    = { .name = "agent_rise",                  .desc = "Number of successful agent checks before declaring a server UP (server 'rise' setting)" },
233         [ST_F_AGENT_FALL]                    = { .name = "agent_fall",                  .desc = "Number of failed agent checks before declaring a server DOWN (server 'fall' setting)" },
234         [ST_F_AGENT_HEALTH]                  = { .name = "agent_health",                .desc = "Current server agent check level (0..fall-1=DOWN, fall..rise-1=UP)" },
235         [ST_F_ADDR]                          = { .name = "addr",                        .desc = "Server's address:port, shown only if show-legends is set, or at levels oper/admin for the CLI" },
236         [ST_F_COOKIE]                        = { .name = "cookie",                      .desc = "Backend's cookie name or Server's cookie value, shown only if show-legends is set, or at levels oper/admin for the CLI" },
237         [ST_F_MODE]                          = { .name = "mode",                        .desc = "'mode' setting (tcp/http/health/cli)" },
238         [ST_F_ALGO]                          = { .name = "algo",                        .desc = "Backend's load balancing algorithm, shown only if show-legends is set, or at levels oper/admin for the CLI" },
239         [ST_F_CONN_RATE]                     = { .name = "conn_rate",                   .desc = "Number of new connections accepted over the last second on the frontend for this worker process" },
240         [ST_F_CONN_RATE_MAX]                 = { .name = "conn_rate_max",               .desc = "Highest value of connections per second observed since the worker process started" },
241         [ST_F_CONN_TOT]                      = { .name = "conn_tot",                    .desc = "Total number of new connections accepted on this frontend since the worker process started" },
242         [ST_F_INTERCEPTED]                   = { .name = "intercepted",                 .desc = "Total number of HTTP requests intercepted on the frontend (redirects/stats/services) since the worker process started" },
243         [ST_F_DCON]                          = { .name = "dcon",                        .desc = "Total number of incoming connections blocked on a listener/frontend by a tcp-request connection rule since the worker process started" },
244         [ST_F_DSES]                          = { .name = "dses",                        .desc = "Total number of incoming sessions blocked on a listener/frontend by a tcp-request connection rule since the worker process started" },
245         [ST_F_WREW]                          = { .name = "wrew",                        .desc = "Total number of failed HTTP header rewrites since the worker process started" },
246         [ST_F_CONNECT]                       = { .name = "connect",                     .desc = "Total number of outgoing connection attempts on this backend/server since the worker process started" },
247         [ST_F_REUSE]                         = { .name = "reuse",                       .desc = "Total number of reused connection on this backend/server since the worker process started" },
248         [ST_F_CACHE_LOOKUPS]                 = { .name = "cache_lookups",               .desc = "Total number of HTTP requests looked up in the cache on this frontend/backend since the worker process started" },
249         [ST_F_CACHE_HITS]                    = { .name = "cache_hits",                  .desc = "Total number of HTTP requests not found in the cache on this frontend/backend since the worker process started" },
250         [ST_F_SRV_ICUR]                      = { .name = "srv_icur",                    .desc = "Current number of idle connections available for reuse on this server" },
251         [ST_F_SRV_ILIM]                      = { .name = "src_ilim",                    .desc = "Limit on the number of available idle connections on this server (server 'pool_max_conn' directive)" },
252         [ST_F_QT_MAX]                        = { .name = "qtime_max",                   .desc = "Maximum observed time spent in the queue, in milliseconds (backend/server)" },
253         [ST_F_CT_MAX]                        = { .name = "ctime_max",                   .desc = "Maximum observed time spent waiting for a connection to complete, in milliseconds (backend/server)" },
254         [ST_F_RT_MAX]                        = { .name = "rtime_max",                   .desc = "Maximum observed time spent waiting for a server response, in milliseconds (backend/server)" },
255         [ST_F_TT_MAX]                        = { .name = "ttime_max",                   .desc = "Maximum observed total request+response time (request+queue+connect+response+processing), in milliseconds (backend/server)" },
256         [ST_F_EINT]                          = { .name = "eint",                        .desc = "Total number of internal errors since process started"},
257         [ST_F_IDLE_CONN_CUR]                 = { .name = "idle_conn_cur",               .desc = "Current number of unsafe idle connections"},
258         [ST_F_SAFE_CONN_CUR]                 = { .name = "safe_conn_cur",               .desc = "Current number of safe idle connections"},
259         [ST_F_USED_CONN_CUR]                 = { .name = "used_conn_cur",               .desc = "Current number of connections in use"},
260         [ST_F_NEED_CONN_EST]                 = { .name = "need_conn_est",               .desc = "Estimated needed number of connections"},
261         [ST_F_UWEIGHT]                       = { .name = "uweight",                     .desc = "Server's user weight, or sum of active servers' user weights for a backend" },
262         [ST_F_AGG_SRV_CHECK_STATUS]          = { .name = "agg_server_check_status",     .desc = "[DEPRECATED] Backend's aggregated gauge of servers' status" },
263         [ST_F_AGG_SRV_STATUS ]               = { .name = "agg_server_status",           .desc = "Backend's aggregated gauge of servers' status" },
264         [ST_F_AGG_CHECK_STATUS]              = { .name = "agg_check_status",            .desc = "Backend's aggregated gauge of servers' state check status" },
265         [ST_F_SRID]                          = { .name = "srid",                        .desc = "Server id revision, to prevent server id reuse mixups" },
266 };
267
268 /* one line of info */
269 THREAD_LOCAL struct field info[INF_TOTAL_FIELDS];
270
271 /* description of statistics (static and dynamic) */
272 static struct name_desc *stat_f[STATS_DOMAIN_COUNT];
273 static size_t stat_count[STATS_DOMAIN_COUNT];
274
275 /* one line for stats */
276 THREAD_LOCAL struct field *stat_l[STATS_DOMAIN_COUNT];
277
278 /* list of all registered stats module */
279 static struct list stats_module_list[STATS_DOMAIN_COUNT] = {
280         LIST_HEAD_INIT(stats_module_list[STATS_DOMAIN_PROXY]),
281         LIST_HEAD_INIT(stats_module_list[STATS_DOMAIN_RESOLVERS]),
282 };
283
284 THREAD_LOCAL void *trash_counters;
285 static THREAD_LOCAL struct buffer trash_chunk = BUF_NULL;
286
287
288 static inline uint8_t stats_get_domain(uint32_t domain)
289 {
290         return domain >> STATS_DOMAIN & STATS_DOMAIN_MASK;
291 }
292
293 static inline enum stats_domain_px_cap stats_px_get_cap(uint32_t domain)
294 {
295         return domain >> STATS_PX_CAP & STATS_PX_CAP_MASK;
296 }
297
298 static void stats_dump_json_schema(struct buffer *out);
299
300 int stats_putchk(struct channel *chn, struct htx *htx, struct buffer *chk)
301 {
302         if (htx) {
303                 if (chk->data >= channel_htx_recv_max(chn, htx))
304                         return 0;
305                 if (!htx_add_data_atonce(htx, ist2(chk->area, chk->data)))
306                         return 0;
307                 channel_add_input(chn, chk->data);
308                 chk->data = 0;
309         }
310         else  {
311                 if (ci_putchk(chn, chk) == -1)
312                         return 0;
313         }
314         return 1;
315 }
316
317 static const char *stats_scope_ptr(struct appctx *appctx, struct stconn *sc)
318 {
319         struct show_stat_ctx *ctx = appctx->svcctx;
320         struct channel *req = sc_oc(sc);
321         struct htx *htx = htxbuf(&req->buf);
322         struct htx_blk *blk;
323         struct ist uri;
324
325         blk = htx_get_head_blk(htx);
326         BUG_ON(!blk || htx_get_blk_type(blk) != HTX_BLK_REQ_SL);
327         ALREADY_CHECKED(blk);
328         uri = htx_sl_req_uri(htx_get_blk_ptr(htx, blk));
329         return uri.ptr + ctx->scope_str;
330 }
331
332 /*
333  * http_stats_io_handler()
334  *     -> stats_dump_stat_to_buffer()     // same as above, but used for CSV or HTML
335  *        -> stats_dump_csv_header()      // emits the CSV headers (same as above)
336  *        -> stats_dump_json_header()     // emits the JSON headers (same as above)
337  *        -> stats_dump_html_head()       // emits the HTML headers
338  *        -> stats_dump_html_info()       // emits the equivalent of "show info" at the top
339  *        -> stats_dump_proxy_to_buffer() // same as above, valid for CSV and HTML
340  *           -> stats_dump_html_px_hdr()
341  *           -> stats_dump_fe_stats()
342  *           -> stats_dump_li_stats()
343  *           -> stats_dump_sv_stats()
344  *           -> stats_dump_be_stats()
345  *           -> stats_dump_html_px_end()
346  *        -> stats_dump_html_end()       // emits HTML trailer
347  *        -> stats_dump_json_end()       // emits JSON trailer
348  */
349
350
351 /* Dumps the stats CSV header to the local trash buffer. The caller is
352  * responsible for clearing it if needed.
353  * NOTE: Some tools happen to rely on the field position instead of its name,
354  *       so please only append new fields at the end, never in the middle.
355  */
356 static void stats_dump_csv_header(enum stats_domain domain)
357 {
358         int field;
359
360         chunk_appendf(&trash_chunk, "# ");
361         if (stat_f[domain]) {
362                 for (field = 0; field < stat_count[domain]; ++field) {
363                         chunk_appendf(&trash_chunk, "%s,", stat_f[domain][field].name);
364
365                         /* print special delimiter on proxy stats to mark end of
366                            static fields */
367                         if (domain == STATS_DOMAIN_PROXY && field + 1 == ST_F_TOTAL_FIELDS)
368                                 chunk_appendf(&trash_chunk, "-,");
369                 }
370         }
371
372         chunk_appendf(&trash_chunk, "\n");
373 }
374
375 /* Emits a stats field without any surrounding element and properly encoded to
376  * resist CSV output. Returns non-zero on success, 0 if the buffer is full.
377  */
378 int stats_emit_raw_data_field(struct buffer *out, const struct field *f)
379 {
380         switch (field_format(f, 0)) {
381         case FF_EMPTY: return 1;
382         case FF_S32:   return chunk_appendf(out, "%d", f->u.s32);
383         case FF_U32:   return chunk_appendf(out, "%u", f->u.u32);
384         case FF_S64:   return chunk_appendf(out, "%lld", (long long)f->u.s64);
385         case FF_U64:   return chunk_appendf(out, "%llu", (unsigned long long)f->u.u64);
386         case FF_FLT:   {
387                 size_t prev_data = out->data;
388                 out->data = flt_trim(out->area, prev_data, chunk_appendf(out, "%f", f->u.flt));
389                 return out->data;
390         }
391         case FF_STR:   return csv_enc_append(field_str(f, 0), 1, out) != NULL;
392         default:       return chunk_appendf(out, "[INCORRECT_FIELD_TYPE_%08x]", f->type);
393         }
394 }
395
396 const char *field_to_html_str(const struct field *f)
397 {
398         switch (field_format(f, 0)) {
399         case FF_S32: return U2H(f->u.s32);
400         case FF_S64: return U2H(f->u.s64);
401         case FF_U64: return U2H(f->u.u64);
402         case FF_U32: return U2H(f->u.u32);
403         case FF_FLT: return F2H(f->u.flt);
404         case FF_STR: return field_str(f, 0);
405         case FF_EMPTY:
406         default:
407                 return "";
408         }
409 }
410
411 /* Emits a stats field prefixed with its type. No CSV encoding is prepared, the
412  * output is supposed to be used on its own line. Returns non-zero on success, 0
413  * if the buffer is full.
414  */
415 int stats_emit_typed_data_field(struct buffer *out, const struct field *f)
416 {
417         switch (field_format(f, 0)) {
418         case FF_EMPTY: return 1;
419         case FF_S32:   return chunk_appendf(out, "s32:%d", f->u.s32);
420         case FF_U32:   return chunk_appendf(out, "u32:%u", f->u.u32);
421         case FF_S64:   return chunk_appendf(out, "s64:%lld", (long long)f->u.s64);
422         case FF_U64:   return chunk_appendf(out, "u64:%llu", (unsigned long long)f->u.u64);
423         case FF_FLT:   {
424                 size_t prev_data = out->data;
425                 out->data = flt_trim(out->area, prev_data, chunk_appendf(out, "flt:%f", f->u.flt));
426                 return out->data;
427         }
428         case FF_STR:   return chunk_appendf(out, "str:%s", field_str(f, 0));
429         default:       return chunk_appendf(out, "%08x:?", f->type);
430         }
431 }
432
433 /* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per
434  * the recommendation for interoperable integers in section 6 of RFC 7159.
435  */
436 #define JSON_INT_MAX ((1ULL << 53) - 1)
437 #define JSON_INT_MIN (0 - JSON_INT_MAX)
438
439 /* Emits a stats field value and its type in JSON.
440  * Returns non-zero on success, 0 on error.
441  */
442 int stats_emit_json_data_field(struct buffer *out, const struct field *f)
443 {
444         int old_len;
445         char buf[20];
446         const char *type, *value = buf, *quote = "";
447
448         switch (field_format(f, 0)) {
449         case FF_EMPTY: return 1;
450         case FF_S32:   type = "\"s32\"";
451                        snprintf(buf, sizeof(buf), "%d", f->u.s32);
452                        break;
453         case FF_U32:   type = "\"u32\"";
454                        snprintf(buf, sizeof(buf), "%u", f->u.u32);
455                        break;
456         case FF_S64:   type = "\"s64\"";
457                        if (f->u.s64 < JSON_INT_MIN || f->u.s64 > JSON_INT_MAX)
458                                return 0;
459                        type = "\"s64\"";
460                        snprintf(buf, sizeof(buf), "%lld", (long long)f->u.s64);
461                        break;
462         case FF_U64:   if (f->u.u64 > JSON_INT_MAX)
463                                return 0;
464                        type = "\"u64\"";
465                        snprintf(buf, sizeof(buf), "%llu",
466                                 (unsigned long long) f->u.u64);
467                        break;
468         case FF_FLT:   type = "\"flt\"";
469                        flt_trim(buf, 0, snprintf(buf, sizeof(buf), "%f", f->u.flt));
470                        break;
471         case FF_STR:   type = "\"str\"";
472                        value = field_str(f, 0);
473                        quote = "\"";
474                        break;
475         default:       snprintf(buf, sizeof(buf), "%u", f->type);
476                        type = buf;
477                        value = "unknown";
478                        quote = "\"";
479                        break;
480         }
481
482         old_len = out->data;
483         chunk_appendf(out, ",\"value\":{\"type\":%s,\"value\":%s%s%s}",
484                       type, quote, value, quote);
485         return !(old_len == out->data);
486 }
487
488 /* Emits an encoding of the field type on 3 characters followed by a delimiter.
489  * Returns non-zero on success, 0 if the buffer is full.
490  */
491 int stats_emit_field_tags(struct buffer *out, const struct field *f,
492                           char delim)
493 {
494         char origin, nature, scope;
495
496         switch (field_origin(f, 0)) {
497         case FO_METRIC:  origin = 'M'; break;
498         case FO_STATUS:  origin = 'S'; break;
499         case FO_KEY:     origin = 'K'; break;
500         case FO_CONFIG:  origin = 'C'; break;
501         case FO_PRODUCT: origin = 'P'; break;
502         default:         origin = '?'; break;
503         }
504
505         switch (field_nature(f, 0)) {
506         case FN_GAUGE:    nature = 'G'; break;
507         case FN_LIMIT:    nature = 'L'; break;
508         case FN_MIN:      nature = 'm'; break;
509         case FN_MAX:      nature = 'M'; break;
510         case FN_RATE:     nature = 'R'; break;
511         case FN_COUNTER:  nature = 'C'; break;
512         case FN_DURATION: nature = 'D'; break;
513         case FN_AGE:      nature = 'A'; break;
514         case FN_TIME:     nature = 'T'; break;
515         case FN_NAME:     nature = 'N'; break;
516         case FN_OUTPUT:   nature = 'O'; break;
517         case FN_AVG:      nature = 'a'; break;
518         default:          nature = '?'; break;
519         }
520
521         switch (field_scope(f, 0)) {
522         case FS_PROCESS: scope = 'P'; break;
523         case FS_SERVICE: scope = 'S'; break;
524         case FS_SYSTEM:  scope = 's'; break;
525         case FS_CLUSTER: scope = 'C'; break;
526         default:         scope = '?'; break;
527         }
528
529         return chunk_appendf(out, "%c%c%c%c", origin, nature, scope, delim);
530 }
531
532 /* Emits an encoding of the field type as JSON.
533   * Returns non-zero on success, 0 if the buffer is full.
534   */
535 int stats_emit_json_field_tags(struct buffer *out, const struct field *f)
536 {
537         const char *origin, *nature, *scope;
538         int old_len;
539
540         switch (field_origin(f, 0)) {
541         case FO_METRIC:  origin = "Metric";  break;
542         case FO_STATUS:  origin = "Status";  break;
543         case FO_KEY:     origin = "Key";     break;
544         case FO_CONFIG:  origin = "Config";  break;
545         case FO_PRODUCT: origin = "Product"; break;
546         default:         origin = "Unknown"; break;
547         }
548
549         switch (field_nature(f, 0)) {
550         case FN_GAUGE:    nature = "Gauge";    break;
551         case FN_LIMIT:    nature = "Limit";    break;
552         case FN_MIN:      nature = "Min";      break;
553         case FN_MAX:      nature = "Max";      break;
554         case FN_RATE:     nature = "Rate";     break;
555         case FN_COUNTER:  nature = "Counter";  break;
556         case FN_DURATION: nature = "Duration"; break;
557         case FN_AGE:      nature = "Age";      break;
558         case FN_TIME:     nature = "Time";     break;
559         case FN_NAME:     nature = "Name";     break;
560         case FN_OUTPUT:   nature = "Output";   break;
561         case FN_AVG:      nature = "Avg";      break;
562         default:          nature = "Unknown";  break;
563         }
564
565         switch (field_scope(f, 0)) {
566         case FS_PROCESS: scope = "Process"; break;
567         case FS_SERVICE: scope = "Service"; break;
568         case FS_SYSTEM:  scope = "System";  break;
569         case FS_CLUSTER: scope = "Cluster"; break;
570         default:         scope = "Unknown"; break;
571         }
572
573         old_len = out->data;
574         chunk_appendf(out, "\"tags\":{"
575                             "\"origin\":\"%s\","
576                             "\"nature\":\"%s\","
577                             "\"scope\":\"%s\""
578                            "}", origin, nature, scope);
579         return !(old_len == out->data);
580 }
581
582 /* Dump all fields from <stats> into <out> using CSV format */
583 static int stats_dump_fields_csv(struct buffer *out,
584                                  const struct field *stats, size_t stats_count,
585                                  struct show_stat_ctx *ctx)
586 {
587         int domain = ctx->domain;
588         int field;
589
590         for (field = 0; field < stats_count; ++field) {
591                 if (!stats_emit_raw_data_field(out, &stats[field]))
592                         return 0;
593                 if (!chunk_strcat(out, ","))
594                         return 0;
595
596                 /* print special delimiter on proxy stats to mark end of
597                    static fields */
598                 if (domain == STATS_DOMAIN_PROXY && field + 1 == ST_F_TOTAL_FIELDS) {
599                         if (!chunk_strcat(out, "-,"))
600                                 return 0;
601                 }
602         }
603
604         chunk_strcat(out, "\n");
605         return 1;
606 }
607
608 /* Dump all fields from <stats> into <out> using a typed "field:desc:type:value" format */
609 static int stats_dump_fields_typed(struct buffer *out,
610                                    const struct field *stats,
611                                    size_t stats_count,
612                                    struct show_stat_ctx * ctx)
613 {
614         int flags = ctx->flags;
615         int domain = ctx->domain;
616         int field;
617
618         for (field = 0; field < stats_count; ++field) {
619                 if (!stats[field].type)
620                         continue;
621
622                 switch (domain) {
623                 case STATS_DOMAIN_PROXY:
624                         chunk_appendf(out, "%c.%u.%u.%d.%s.%u:",
625                                       stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE ? 'F' :
626                                       stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE ? 'B' :
627                                       stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO ? 'L' :
628                                       stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV ? 'S' :
629                                       '?',
630                                       stats[ST_F_IID].u.u32, stats[ST_F_SID].u.u32,
631                                       field,
632                                       stat_f[domain][field].name,
633                                       stats[ST_F_PID].u.u32);
634                         break;
635
636                 case STATS_DOMAIN_RESOLVERS:
637                         chunk_appendf(out, "N.%d.%s:", field,
638                                       stat_f[domain][field].name);
639                         break;
640
641                 default:
642                         break;
643                 }
644
645                 if (!stats_emit_field_tags(out, &stats[field], ':'))
646                         return 0;
647                 if (!stats_emit_typed_data_field(out, &stats[field]))
648                         return 0;
649
650                 if (flags & STAT_SHOW_FDESC &&
651                     !chunk_appendf(out, ":\"%s\"", stat_f[domain][field].desc)) {
652                         return 0;
653                 }
654
655                 if (!chunk_strcat(out, "\n"))
656                         return 0;
657         }
658         return 1;
659 }
660
661 /* Dump all fields from <stats> into <out> using the "show info json" format */
662 static int stats_dump_json_info_fields(struct buffer *out,
663                                        const struct field *info,
664                                        struct show_stat_ctx *ctx)
665 {
666         int started = (ctx->field) ? 1 : 0;
667         int ready_data = 0;
668
669         if (!started && !chunk_strcat(out, "["))
670                 return 0;
671
672         for (; ctx->field < INF_TOTAL_FIELDS; ctx->field++) {
673                 int old_len;
674                 int field = ctx->field;
675
676                 if (!field_format(info, field))
677                         continue;
678
679                 if (started && !chunk_strcat(out, ","))
680                         goto err;
681                 started = 1;
682
683                 old_len = out->data;
684                 chunk_appendf(out,
685                               "{\"field\":{\"pos\":%d,\"name\":\"%s\"},"
686                               "\"processNum\":%u,",
687                               field, info_fields[field].name,
688                               info[INF_PROCESS_NUM].u.u32);
689                 if (old_len == out->data)
690                         goto err;
691
692                 if (!stats_emit_json_field_tags(out, &info[field]))
693                         goto err;
694
695                 if (!stats_emit_json_data_field(out, &info[field]))
696                         goto err;
697
698                 if (!chunk_strcat(out, "}"))
699                         goto err;
700                 ready_data = out->data;
701         }
702
703         if (!chunk_strcat(out, "]\n"))
704                 goto err;
705         ctx->field = 0; /* we're done */
706         return 1;
707
708 err:
709         if (!ready_data) {
710                 /* not enough buffer space for a single entry.. */
711                 chunk_reset(out);
712                 chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}\n");
713                 return 0; /* hard error */
714         }
715         /* push ready data and wait for a new buffer to complete the dump */
716         out->data = ready_data;
717         return 1;
718 }
719
720 static void stats_print_proxy_field_json(struct buffer *out,
721                                          const struct field *stat,
722                                          const char *name,
723                                          int pos,
724                                          uint32_t field_type,
725                                          uint32_t iid,
726                                          uint32_t sid,
727                                          uint32_t pid)
728 {
729         const char *obj_type;
730         switch (field_type) {
731                 case STATS_TYPE_FE: obj_type = "Frontend"; break;
732                 case STATS_TYPE_BE: obj_type = "Backend";  break;
733                 case STATS_TYPE_SO: obj_type = "Listener"; break;
734                 case STATS_TYPE_SV: obj_type = "Server";   break;
735                 default:            obj_type = "Unknown";  break;
736         }
737
738         chunk_appendf(out,
739                       "{"
740                       "\"objType\":\"%s\","
741                       "\"proxyId\":%u,"
742                       "\"id\":%u,"
743                       "\"field\":{\"pos\":%d,\"name\":\"%s\"},"
744                       "\"processNum\":%u,",
745                       obj_type, iid, sid, pos, name, pid);
746 }
747
748 static void stats_print_rslv_field_json(struct buffer *out,
749                                         const struct field *stat,
750                                         const char *name,
751                                         int pos)
752 {
753         chunk_appendf(out,
754                       "{"
755                       "\"field\":{\"pos\":%d,\"name\":\"%s\"},",
756                       pos, name);
757 }
758
759
760 /* Dump all fields from <stats> into <out> using a typed "field:desc:type:value" format */
761 static int stats_dump_fields_json(struct buffer *out,
762                                   const struct field *stats, size_t stats_count,
763                                   struct show_stat_ctx *ctx)
764 {
765         int flags = ctx->flags;
766         int domain = ctx->domain;
767         int started = (ctx->field) ? 1 : 0;
768         int ready_data = 0;
769
770         if (!started && (flags & STAT_STARTED) && !chunk_strcat(out, ","))
771                 return 0;
772         if (!started && !chunk_strcat(out, "["))
773                 return 0;
774
775         for (; ctx->field < stats_count; ctx->field++) {
776                 int old_len;
777                 int field = ctx->field;
778
779                 if (!stats[field].type)
780                         continue;
781
782                 if (started && !chunk_strcat(out, ","))
783                         goto err;
784                 started = 1;
785
786                 old_len = out->data;
787                 if (domain == STATS_DOMAIN_PROXY) {
788                         stats_print_proxy_field_json(out, &stats[field],
789                                                      stat_f[domain][field].name,
790                                                      field,
791                                                      stats[ST_F_TYPE].u.u32,
792                                                      stats[ST_F_IID].u.u32,
793                                                      stats[ST_F_SID].u.u32,
794                                                      stats[ST_F_PID].u.u32);
795                 } else if (domain == STATS_DOMAIN_RESOLVERS) {
796                         stats_print_rslv_field_json(out, &stats[field],
797                                                     stat_f[domain][field].name,
798                                                     field);
799                 }
800
801                 if (old_len == out->data)
802                         goto err;
803
804                 if (!stats_emit_json_field_tags(out, &stats[field]))
805                         goto err;
806
807                 if (!stats_emit_json_data_field(out, &stats[field]))
808                         goto err;
809
810                 if (!chunk_strcat(out, "}"))
811                         goto err;
812                 ready_data = out->data;
813         }
814
815         if (!chunk_strcat(out, "]"))
816                 goto err;
817
818         ctx->field = 0; /* we're done */
819         return 1;
820
821 err:
822         if (!ready_data) {
823                 /* not enough buffer space for a single entry.. */
824                 chunk_reset(out);
825                 if (ctx->flags & STAT_STARTED)
826                         chunk_strcat(out, ",");
827                 chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}");
828                 return 0; /* hard error */
829         }
830         /* push ready data and wait for a new buffer to complete the dump */
831         out->data = ready_data;
832         return 1;
833 }
834
835 /* Dump all fields from <stats> into <out> using the HTML format. A column is
836  * reserved for the checkbox is STAT_ADMIN is set in <flags>. Some extra info
837  * are provided if STAT_SHLGNDS is present in <flags>. The statistics from
838  * extra modules are displayed at the end of the lines if STAT_SHMODULES is
839  * present in <flags>.
840  */
841 static int stats_dump_fields_html(struct buffer *out,
842                                   const struct field *stats,
843                                   struct show_stat_ctx *ctx)
844 {
845         struct buffer src;
846         struct stats_module *mod;
847         int flags = ctx->flags;
848         int i = 0, j = 0;
849
850         if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE) {
851                 chunk_appendf(out,
852                               /* name, queue */
853                               "<tr class=\"frontend\">");
854
855                 if (flags & STAT_ADMIN) {
856                         /* Column sub-heading for Enable or Disable server */
857                         chunk_appendf(out, "<td></td>");
858                 }
859
860                 chunk_appendf(out,
861                               "<td class=ac>"
862                               "<a name=\"%s/Frontend\"></a>"
863                               "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
864                               "<td colspan=3></td>"
865                               "",
866                               field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_PXNAME));
867
868                 chunk_appendf(out,
869                               /* sessions rate : current */
870                               "<td><u>%s<div class=tips><table class=det>"
871                               "<tr><th>Current connection rate:</th><td>%s/s</td></tr>"
872                               "<tr><th>Current session rate:</th><td>%s/s</td></tr>"
873                               "",
874                               U2H(stats[ST_F_RATE].u.u32),
875                               U2H(stats[ST_F_CONN_RATE].u.u32),
876                               U2H(stats[ST_F_RATE].u.u32));
877
878                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
879                         chunk_appendf(out,
880                                       "<tr><th>Current request rate:</th><td>%s/s</td></tr>",
881                                       U2H(stats[ST_F_REQ_RATE].u.u32));
882
883                 chunk_appendf(out,
884                               "</table></div></u></td>"
885                               /* sessions rate : max */
886                               "<td><u>%s<div class=tips><table class=det>"
887                               "<tr><th>Max connection rate:</th><td>%s/s</td></tr>"
888                               "<tr><th>Max session rate:</th><td>%s/s</td></tr>"
889                               "",
890                               U2H(stats[ST_F_RATE_MAX].u.u32),
891                               U2H(stats[ST_F_CONN_RATE_MAX].u.u32),
892                               U2H(stats[ST_F_RATE_MAX].u.u32));
893
894                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
895                         chunk_appendf(out,
896                                       "<tr><th>Max request rate:</th><td>%s/s</td></tr>",
897                                       U2H(stats[ST_F_REQ_RATE_MAX].u.u32));
898
899                 chunk_appendf(out,
900                               "</table></div></u></td>"
901                               /* sessions rate : limit */
902                               "<td>%s</td>",
903                               LIM2A(stats[ST_F_RATE_LIM].u.u32, "-"));
904
905                 chunk_appendf(out,
906                               /* sessions: current, max, limit, total */
907                               "<td>%s</td><td>%s</td><td>%s</td>"
908                               "<td><u>%s<div class=tips><table class=det>"
909                               "<tr><th>Cum. connections:</th><td>%s</td></tr>"
910                               "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
911                               "",
912                               U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
913                               U2H(stats[ST_F_STOT].u.u64),
914                               U2H(stats[ST_F_CONN_TOT].u.u64),
915                               U2H(stats[ST_F_STOT].u.u64));
916
917                 /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
918                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
919                         chunk_appendf(out,
920                                       "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
921                                       "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
922                                       "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
923                                       "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
924                                       "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
925                                       "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
926                                       "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
927                                       "<tr><th>- other responses:</th><td>%s</td></tr>"
928                                       "<tr><th>Intercepted requests:</th><td>%s</td></tr>"
929                                       "<tr><th>Cache lookups:</th><td>%s</td></tr>"
930                                       "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
931                                       "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
932                                       "<tr><th>Internal errors:</th><td>%s</td></tr>"
933                                       "",
934                                       U2H(stats[ST_F_REQ_TOT].u.u64),
935                                       U2H(stats[ST_F_HRSP_1XX].u.u64),
936                                       U2H(stats[ST_F_HRSP_2XX].u.u64),
937                                       U2H(stats[ST_F_COMP_RSP].u.u64),
938                                       stats[ST_F_HRSP_2XX].u.u64 ?
939                                       (int)(100 * stats[ST_F_COMP_RSP].u.u64 / stats[ST_F_HRSP_2XX].u.u64) : 0,
940                                       U2H(stats[ST_F_HRSP_3XX].u.u64),
941                                       U2H(stats[ST_F_HRSP_4XX].u.u64),
942                                       U2H(stats[ST_F_HRSP_5XX].u.u64),
943                                       U2H(stats[ST_F_HRSP_OTHER].u.u64),
944                                       U2H(stats[ST_F_INTERCEPTED].u.u64),
945                                       U2H(stats[ST_F_CACHE_LOOKUPS].u.u64),
946                                       U2H(stats[ST_F_CACHE_HITS].u.u64),
947                                       stats[ST_F_CACHE_LOOKUPS].u.u64 ?
948                                       (int)(100 * stats[ST_F_CACHE_HITS].u.u64 / stats[ST_F_CACHE_LOOKUPS].u.u64) : 0,
949                                       U2H(stats[ST_F_WREW].u.u64),
950                                       U2H(stats[ST_F_EINT].u.u64));
951                 }
952
953                 chunk_appendf(out,
954                               "</table></div></u></td>"
955                               /* sessions: lbtot, lastsess */
956                               "<td></td><td></td>"
957                               /* bytes : in */
958                               "<td>%s</td>"
959                               "",
960                               U2H(stats[ST_F_BIN].u.u64));
961
962                 chunk_appendf(out,
963                               /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
964                               "<td>%s%s<div class=tips><table class=det>"
965                               "<tr><th>Response bytes in:</th><td>%s</td></tr>"
966                               "<tr><th>Compression in:</th><td>%s</td></tr>"
967                               "<tr><th>Compression out:</th><td>%s</td><td>(%d%%)</td></tr>"
968                               "<tr><th>Compression bypass:</th><td>%s</td></tr>"
969                               "<tr><th>Total bytes saved:</th><td>%s</td><td>(%d%%)</td></tr>"
970                               "</table></div>%s</td>",
971                               (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "<u>":"",
972                               U2H(stats[ST_F_BOUT].u.u64),
973                               U2H(stats[ST_F_BOUT].u.u64),
974                               U2H(stats[ST_F_COMP_IN].u.u64),
975                               U2H(stats[ST_F_COMP_OUT].u.u64),
976                               stats[ST_F_COMP_IN].u.u64 ? (int)(stats[ST_F_COMP_OUT].u.u64 * 100 / stats[ST_F_COMP_IN].u.u64) : 0,
977                               U2H(stats[ST_F_COMP_BYP].u.u64),
978                               U2H(stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64),
979                               stats[ST_F_BOUT].u.u64 ? (int)((stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64) * 100 / stats[ST_F_BOUT].u.u64) : 0,
980                               (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "</u>":"");
981
982                 chunk_appendf(out,
983                               /* denied: req, resp */
984                               "<td>%s</td><td>%s</td>"
985                               /* errors : request, connect, response */
986                               "<td>%s</td><td></td><td></td>"
987                               /* warnings: retries, redispatches */
988                               "<td></td><td></td>"
989                               /* server status : reflect frontend status */
990                               "<td class=ac>%s</td>"
991                               /* rest of server: nothing */
992                               "<td class=ac colspan=8></td>"
993                               "",
994                               U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
995                               U2H(stats[ST_F_EREQ].u.u64),
996                               field_str(stats, ST_F_STATUS));
997
998                 if (flags & STAT_SHMODULES) {
999                         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
1000                                 chunk_appendf(out, "<td>");
1001
1002                                 if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE) {
1003                                         chunk_appendf(out,
1004                                                       "<u>%s<div class=tips><table class=det>",
1005                                                       mod->name);
1006                                         for (j = 0; j < mod->stats_count; ++j) {
1007                                                 chunk_appendf(out,
1008                                                               "<tr><th>%s</th><td>%s</td></tr>",
1009                                                               mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
1010                                                 ++i;
1011                                         }
1012                                         chunk_appendf(out, "</table></div></u>");
1013                                 } else {
1014                                         i += mod->stats_count;
1015                                 }
1016
1017                                 chunk_appendf(out, "</td>");
1018                         }
1019                 }
1020
1021                 chunk_appendf(out, "</tr>");
1022         }
1023         else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO) {
1024                 chunk_appendf(out, "<tr class=socket>");
1025                 if (flags & STAT_ADMIN) {
1026                         /* Column sub-heading for Enable or Disable server */
1027                         chunk_appendf(out, "<td></td>");
1028                 }
1029
1030                 chunk_appendf(out,
1031                               /* frontend name, listener name */
1032                               "<td class=ac><a name=\"%s/+%s\"></a>%s"
1033                               "<a class=lfsb href=\"#%s/+%s\">%s</a>"
1034                               "",
1035                               field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME),
1036                               (flags & STAT_SHLGNDS)?"<u>":"",
1037                               field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME), field_str(stats, ST_F_SVNAME));
1038
1039                 if (flags & STAT_SHLGNDS) {
1040                         chunk_appendf(out, "<div class=tips>");
1041
1042                         if (isdigit((unsigned char)*field_str(stats, ST_F_ADDR)))
1043                                 chunk_appendf(out, "IPv4: %s, ", field_str(stats, ST_F_ADDR));
1044                         else if (*field_str(stats, ST_F_ADDR) == '[')
1045                                 chunk_appendf(out, "IPv6: %s, ", field_str(stats, ST_F_ADDR));
1046                         else if (*field_str(stats, ST_F_ADDR))
1047                                 chunk_appendf(out, "%s, ", field_str(stats, ST_F_ADDR));
1048
1049                         /* id */
1050                         chunk_appendf(out, "id: %d</div>", stats[ST_F_SID].u.u32);
1051                 }
1052
1053                 chunk_appendf(out,
1054                               /* queue */
1055                               "%s</td><td colspan=3></td>"
1056                               /* sessions rate: current, max, limit */
1057                               "<td colspan=3>&nbsp;</td>"
1058                               /* sessions: current, max, limit, total, lbtot, lastsess */
1059                               "<td>%s</td><td>%s</td><td>%s</td>"
1060                               "<td>%s</td><td>&nbsp;</td><td>&nbsp;</td>"
1061                               /* bytes: in, out */
1062                               "<td>%s</td><td>%s</td>"
1063                               "",
1064                               (flags & STAT_SHLGNDS)?"</u>":"",
1065                               U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
1066                               U2H(stats[ST_F_STOT].u.u64), U2H(stats[ST_F_BIN].u.u64), U2H(stats[ST_F_BOUT].u.u64));
1067
1068                 chunk_appendf(out,
1069                               /* denied: req, resp */
1070                               "<td>%s</td><td>%s</td>"
1071                               /* errors: request, connect, response */
1072                               "<td>%s</td><td></td><td></td>"
1073                               /* warnings: retries, redispatches */
1074                               "<td></td><td></td>"
1075                               /* server status: reflect listener status */
1076                               "<td class=ac>%s</td>"
1077                               /* rest of server: nothing */
1078                               "<td class=ac colspan=8></td>"
1079                               "",
1080                               U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
1081                               U2H(stats[ST_F_EREQ].u.u64),
1082                               field_str(stats, ST_F_STATUS));
1083
1084                 if (flags & STAT_SHMODULES) {
1085                         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
1086                                 chunk_appendf(out, "<td>");
1087
1088                                 if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI) {
1089                                         chunk_appendf(out,
1090                                                       "<u>%s<div class=tips><table class=det>",
1091                                                       mod->name);
1092                                         for (j = 0; j < mod->stats_count; ++j) {
1093                                                 chunk_appendf(out,
1094                                                               "<tr><th>%s</th><td>%s</td></tr>",
1095                                                               mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
1096                                                 ++i;
1097                                         }
1098                                         chunk_appendf(out, "</table></div></u>");
1099                                 } else {
1100                                         i += mod->stats_count;
1101                                 }
1102
1103                                 chunk_appendf(out, "</td>");
1104                         }
1105                 }
1106
1107                 chunk_appendf(out, "</tr>");
1108         }
1109         else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV) {
1110                 const char *style;
1111
1112                 /* determine the style to use depending on the server's state,
1113                  * its health and weight. There isn't a 1-to-1 mapping between
1114                  * state and styles for the cases where the server is (still)
1115                  * up. The reason is that we don't want to report nolb and
1116                  * drain with the same color.
1117                  */
1118
1119                 if (strcmp(field_str(stats, ST_F_STATUS), "DOWN") == 0 ||
1120                     strcmp(field_str(stats, ST_F_STATUS), "DOWN (agent)") == 0) {
1121                         style = "down";
1122                 }
1123                 else if (strncmp(field_str(stats, ST_F_STATUS), "DOWN ", strlen("DOWN ")) == 0) {
1124                         style = "going_up";
1125                 }
1126                 else if (strcmp(field_str(stats, ST_F_STATUS), "DRAIN") == 0) {
1127                         style = "draining";
1128                 }
1129                 else if (strncmp(field_str(stats, ST_F_STATUS), "NOLB ", strlen("NOLB ")) == 0) {
1130                         style = "going_down";
1131                 }
1132                 else if (strcmp(field_str(stats, ST_F_STATUS), "NOLB") == 0) {
1133                         style = "nolb";
1134                 }
1135                 else if (strcmp(field_str(stats, ST_F_STATUS), "no check") == 0) {
1136                         style = "no_check";
1137                 }
1138                 else if (!stats[ST_F_CHKFAIL].type ||
1139                          stats[ST_F_CHECK_HEALTH].u.u32 == stats[ST_F_CHECK_RISE].u.u32 + stats[ST_F_CHECK_FALL].u.u32 - 1) {
1140                         /* no check or max health = UP */
1141                         if (stats[ST_F_WEIGHT].u.u32)
1142                                 style = "up";
1143                         else
1144                                 style = "draining";
1145                 }
1146                 else {
1147                         style = "going_down";
1148                 }
1149
1150                 if (strncmp(field_str(stats, ST_F_STATUS), "MAINT", 5) == 0)
1151                         chunk_appendf(out, "<tr class=\"maintain\">");
1152                 else
1153                         chunk_appendf(out,
1154                                       "<tr class=\"%s_%s\">",
1155                                       (stats[ST_F_BCK].u.u32) ? "backup" : "active", style);
1156
1157
1158                 if (flags & STAT_ADMIN)
1159                         chunk_appendf(out,
1160                                       "<td><input class='%s-checkbox' type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
1161                                       field_str(stats, ST_F_PXNAME),
1162                                       field_str(stats, ST_F_SVNAME));
1163
1164                 chunk_appendf(out,
1165                               "<td class=ac><a name=\"%s/%s\"></a>%s"
1166                               "<a class=lfsb href=\"#%s/%s\">%s</a>"
1167                               "",
1168                               field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME),
1169                               (flags & STAT_SHLGNDS) ? "<u>" : "",
1170                               field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME), field_str(stats, ST_F_SVNAME));
1171
1172                 if (flags & STAT_SHLGNDS) {
1173                         chunk_appendf(out, "<div class=tips>");
1174
1175                         if (isdigit((unsigned char)*field_str(stats, ST_F_ADDR)))
1176                                 chunk_appendf(out, "IPv4: %s, ", field_str(stats, ST_F_ADDR));
1177                         else if (*field_str(stats, ST_F_ADDR) == '[')
1178                                 chunk_appendf(out, "IPv6: %s, ", field_str(stats, ST_F_ADDR));
1179                         else if (*field_str(stats, ST_F_ADDR))
1180                                 chunk_appendf(out, "%s, ", field_str(stats, ST_F_ADDR));
1181
1182                         /* id */
1183                         chunk_appendf(out, "id: %d, rid: %d", stats[ST_F_SID].u.u32, stats[ST_F_SRID].u.u32);
1184
1185                         /* cookie */
1186                         if (stats[ST_F_COOKIE].type) {
1187                                 chunk_appendf(out, ", cookie: '");
1188                                 chunk_initstr(&src, field_str(stats, ST_F_COOKIE));
1189                                 chunk_htmlencode(out, &src);
1190                                 chunk_appendf(out, "'");
1191                         }
1192
1193                         chunk_appendf(out, "</div>");
1194                 }
1195
1196                 chunk_appendf(out,
1197                               /* queue : current, max, limit */
1198                               "%s</td><td>%s</td><td>%s</td><td>%s</td>"
1199                               /* sessions rate : current, max, limit */
1200                               "<td>%s</td><td>%s</td><td></td>"
1201                               "",
1202                               (flags & STAT_SHLGNDS) ? "</u>" : "",
1203                               U2H(stats[ST_F_QCUR].u.u32), U2H(stats[ST_F_QMAX].u.u32), LIM2A(stats[ST_F_QLIMIT].u.u32, "-"),
1204                               U2H(stats[ST_F_RATE].u.u32), U2H(stats[ST_F_RATE_MAX].u.u32));
1205
1206                 chunk_appendf(out,
1207                               /* sessions: current, max, limit, total */
1208                               "<td><u>%s<div class=tips>"
1209                                 "<table class=det>"
1210                                 "<tr><th>Current active connections:</th><td>%s</td></tr>"
1211                                 "<tr><th>Current used connections:</th><td>%s</td></tr>"
1212                                 "<tr><th>Current idle connections:</th><td>%s</td></tr>"
1213                                 "<tr><th>- unsafe:</th><td>%s</td></tr>"
1214                                 "<tr><th>- safe:</th><td>%s</td></tr>"
1215                                 "<tr><th>Estimated need of connections:</th><td>%s</td></tr>"
1216                                 "<tr><th>Active connections limit:</th><td>%s</td></tr>"
1217                                 "<tr><th>Idle connections limit:</th><td>%s</td></tr>"
1218                                 "</table></div></u>"
1219                               "</td><td>%s</td><td>%s</td>"
1220                               "<td><u>%s<div class=tips><table class=det>"
1221                               "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
1222                               "",
1223                               U2H(stats[ST_F_SCUR].u.u32),
1224                               U2H(stats[ST_F_SCUR].u.u32),
1225                               U2H(stats[ST_F_USED_CONN_CUR].u.u32),
1226                               U2H(stats[ST_F_SRV_ICUR].u.u32),
1227                               U2H(stats[ST_F_IDLE_CONN_CUR].u.u32),
1228                               U2H(stats[ST_F_SAFE_CONN_CUR].u.u32),
1229                               U2H(stats[ST_F_NEED_CONN_EST].u.u32),
1230
1231                                 LIM2A(stats[ST_F_SLIM].u.u32, "-"),
1232                                 stats[ST_F_SRV_ILIM].type ? U2H(stats[ST_F_SRV_ILIM].u.u32) : "-",
1233                               U2H(stats[ST_F_SMAX].u.u32), LIM2A(stats[ST_F_SLIM].u.u32, "-"),
1234                               U2H(stats[ST_F_STOT].u.u64),
1235                               U2H(stats[ST_F_STOT].u.u64));
1236
1237                 /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1238                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
1239                         chunk_appendf(out,
1240                                       "<tr><th>New connections:</th><td>%s</td></tr>"
1241                                       "<tr><th>Reused connections:</th><td>%s</td><td>(%d%%)</td></tr>"
1242                                       "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
1243                                       "<tr><th>- HTTP 1xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
1244                                       "<tr><th>- HTTP 2xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
1245                                       "<tr><th>- HTTP 3xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
1246                                       "<tr><th>- HTTP 4xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
1247                                       "<tr><th>- HTTP 5xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
1248                                       "<tr><th>- other responses:</th><td>%s</td><td>(%d%%)</td></tr>"
1249                                       "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
1250                                       "<tr><th>Internal error:</th><td>%s</td></tr>"
1251                                       "",
1252                                       U2H(stats[ST_F_CONNECT].u.u64),
1253                                       U2H(stats[ST_F_REUSE].u.u64),
1254                                       (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64) ?
1255                                       (int)(100 * stats[ST_F_REUSE].u.u64 / (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64)) : 0,
1256                                       U2H(stats[ST_F_REQ_TOT].u.u64),
1257                                       U2H(stats[ST_F_HRSP_1XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
1258                                       (int)(100 * stats[ST_F_HRSP_1XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
1259                                       U2H(stats[ST_F_HRSP_2XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
1260                                       (int)(100 * stats[ST_F_HRSP_2XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
1261                                       U2H(stats[ST_F_HRSP_3XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
1262                                       (int)(100 * stats[ST_F_HRSP_3XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
1263                                       U2H(stats[ST_F_HRSP_4XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
1264                                       (int)(100 * stats[ST_F_HRSP_4XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
1265                                       U2H(stats[ST_F_HRSP_5XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
1266                                       (int)(100 * stats[ST_F_HRSP_5XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
1267                                       U2H(stats[ST_F_HRSP_OTHER].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
1268                                       (int)(100 * stats[ST_F_HRSP_OTHER].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
1269                                       U2H(stats[ST_F_WREW].u.u64),
1270                                       U2H(stats[ST_F_EINT].u.u64));
1271                 }
1272
1273                 chunk_appendf(out, "<tr><th colspan=3>Max / Avg over last 1024 success. conn.</th></tr>");
1274                 chunk_appendf(out, "<tr><th>- Queue time:</th><td>%s / %s</td><td>ms</td></tr>",
1275                               U2H(stats[ST_F_QT_MAX].u.u32), U2H(stats[ST_F_QTIME].u.u32));
1276                 chunk_appendf(out, "<tr><th>- Connect time:</th><td>%s / %s</td><td>ms</td></tr>",
1277                               U2H(stats[ST_F_CT_MAX].u.u32), U2H(stats[ST_F_CTIME].u.u32));
1278                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
1279                         chunk_appendf(out, "<tr><th>- Responses time:</th><td>%s / %s</td><td>ms</td></tr>",
1280                                       U2H(stats[ST_F_RT_MAX].u.u32), U2H(stats[ST_F_RTIME].u.u32));
1281                 chunk_appendf(out, "<tr><th>- Total time:</th><td>%s / %s</td><td>ms</td></tr>",
1282                               U2H(stats[ST_F_TT_MAX].u.u32), U2H(stats[ST_F_TTIME].u.u32));
1283
1284                 chunk_appendf(out,
1285                               "</table></div></u></td>"
1286                               /* sessions: lbtot, last */
1287                               "<td>%s</td><td>%s</td>",
1288                               U2H(stats[ST_F_LBTOT].u.u64),
1289                               human_time(stats[ST_F_LASTSESS].u.s32, 1));
1290
1291                 chunk_appendf(out,
1292                               /* bytes : in, out */
1293                               "<td>%s</td><td>%s</td>"
1294                               /* denied: req, resp */
1295                               "<td></td><td>%s</td>"
1296                               /* errors : request, connect */
1297                               "<td></td><td>%s</td>"
1298                               /* errors : response */
1299                               "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
1300                               /* warnings: retries, redispatches */
1301                               "<td>%lld</td><td>%lld</td>"
1302                               "",
1303                               U2H(stats[ST_F_BIN].u.u64), U2H(stats[ST_F_BOUT].u.u64),
1304                               U2H(stats[ST_F_DRESP].u.u64),
1305                               U2H(stats[ST_F_ECON].u.u64),
1306                               U2H(stats[ST_F_ERESP].u.u64),
1307                               (long long)stats[ST_F_CLI_ABRT].u.u64,
1308                               (long long)stats[ST_F_SRV_ABRT].u.u64,
1309                               (long long)stats[ST_F_WRETR].u.u64,
1310                               (long long)stats[ST_F_WREDIS].u.u64);
1311
1312                 /* status, last change */
1313                 chunk_appendf(out, "<td class=ac>");
1314
1315                 /* FIXME!!!!
1316                  *   LASTCHG should contain the last change for *this* server and must be computed
1317                  * properly above, as was done below, ie: this server if maint, otherwise ref server
1318                  * if tracking. Note that ref is either local or remote depending on tracking.
1319                  */
1320
1321
1322                 if (strncmp(field_str(stats, ST_F_STATUS), "MAINT", 5) == 0) {
1323                         chunk_appendf(out, "%s MAINT", human_time(stats[ST_F_LASTCHG].u.u32, 1));
1324                 }
1325                 else if (strcmp(field_str(stats, ST_F_STATUS), "no check") == 0) {
1326                         chunk_strcat(out, "<i>no check</i>");
1327                 }
1328                 else {
1329                         chunk_appendf(out, "%s %s", human_time(stats[ST_F_LASTCHG].u.u32, 1), field_str(stats, ST_F_STATUS));
1330                         if (strncmp(field_str(stats, ST_F_STATUS), "DOWN", 4) == 0) {
1331                                 if (stats[ST_F_CHECK_HEALTH].u.u32)
1332                                         chunk_strcat(out, " &uarr;");
1333                         }
1334                         else if (stats[ST_F_CHECK_HEALTH].u.u32 < stats[ST_F_CHECK_RISE].u.u32 + stats[ST_F_CHECK_FALL].u.u32 - 1)
1335                                 chunk_strcat(out, " &darr;");
1336                 }
1337
1338                 if (strncmp(field_str(stats, ST_F_STATUS), "DOWN", 4) == 0 &&
1339                     stats[ST_F_AGENT_STATUS].type && !stats[ST_F_AGENT_HEALTH].u.u32) {
1340                         chunk_appendf(out,
1341                                       "</td><td class=ac><u> %s",
1342                                       field_str(stats, ST_F_AGENT_STATUS));
1343
1344                         if (stats[ST_F_AGENT_CODE].type)
1345                                 chunk_appendf(out, "/%d", stats[ST_F_AGENT_CODE].u.u32);
1346
1347                         if (stats[ST_F_AGENT_DURATION].type)
1348                                 chunk_appendf(out, " in %lums", (long)stats[ST_F_AGENT_DURATION].u.u64);
1349
1350                         chunk_appendf(out, "<div class=tips>%s", field_str(stats, ST_F_AGENT_DESC));
1351
1352                         if (*field_str(stats, ST_F_LAST_AGT)) {
1353                                 chunk_appendf(out, ": ");
1354                                 chunk_initstr(&src, field_str(stats, ST_F_LAST_AGT));
1355                                 chunk_htmlencode(out, &src);
1356                         }
1357                         chunk_appendf(out, "</div></u>");
1358                 }
1359                 else if (stats[ST_F_CHECK_STATUS].type) {
1360                         chunk_appendf(out,
1361                                       "</td><td class=ac><u> %s",
1362                                       field_str(stats, ST_F_CHECK_STATUS));
1363
1364                         if (stats[ST_F_CHECK_CODE].type)
1365                                 chunk_appendf(out, "/%d", stats[ST_F_CHECK_CODE].u.u32);
1366
1367                         if (stats[ST_F_CHECK_DURATION].type)
1368                                 chunk_appendf(out, " in %lums", (long)stats[ST_F_CHECK_DURATION].u.u64);
1369
1370                         chunk_appendf(out, "<div class=tips>%s", field_str(stats, ST_F_CHECK_DESC));
1371
1372                         if (*field_str(stats, ST_F_LAST_CHK)) {
1373                                 chunk_appendf(out, ": ");
1374                                 chunk_initstr(&src, field_str(stats, ST_F_LAST_CHK));
1375                                 chunk_htmlencode(out, &src);
1376                         }
1377                         chunk_appendf(out, "</div></u>");
1378                 }
1379                 else
1380                         chunk_appendf(out, "</td><td>");
1381
1382                 chunk_appendf(out,
1383                               /* weight / uweight */
1384                               "</td><td class=ac>%d/%d</td>"
1385                               /* act, bck */
1386                               "<td class=ac>%s</td><td class=ac>%s</td>"
1387                               "",
1388                               stats[ST_F_WEIGHT].u.u32, stats[ST_F_UWEIGHT].u.u32,
1389                               stats[ST_F_BCK].u.u32 ? "-" : "Y",
1390                               stats[ST_F_BCK].u.u32 ? "Y" : "-");
1391
1392                 /* check failures: unique, fatal, down time */
1393                 if (strcmp(field_str(stats, ST_F_STATUS), "MAINT (resolution)") == 0) {
1394                         chunk_appendf(out, "<td class=ac colspan=3>resolution</td>");
1395                 }
1396                 else if (stats[ST_F_CHKFAIL].type) {
1397                         chunk_appendf(out, "<td><u>%lld", (long long)stats[ST_F_CHKFAIL].u.u64);
1398
1399                         if (stats[ST_F_HANAFAIL].type)
1400                                 chunk_appendf(out, "/%lld", (long long)stats[ST_F_HANAFAIL].u.u64);
1401
1402                         chunk_appendf(out,
1403                                       "<div class=tips>Failed Health Checks%s</div></u></td>"
1404                                       "<td>%lld</td><td>%s</td>"
1405                                       "",
1406                                       stats[ST_F_HANAFAIL].type ? "/Health Analyses" : "",
1407                                       (long long)stats[ST_F_CHKDOWN].u.u64, human_time(stats[ST_F_DOWNTIME].u.u32, 1));
1408                 }
1409                 else if (strcmp(field_str(stats, ST_F_STATUS), "MAINT") != 0 && field_format(stats, ST_F_TRACKED) == FF_STR) {
1410                         /* tracking a server (hence inherited maint would appear as "MAINT (via...)" */
1411                         chunk_appendf(out,
1412                                       "<td class=ac colspan=3><a class=lfsb href=\"#%s\">via %s</a></td>",
1413                                       field_str(stats, ST_F_TRACKED), field_str(stats, ST_F_TRACKED));
1414                 }
1415                 else
1416                         chunk_appendf(out, "<td colspan=3></td>");
1417
1418                 /* throttle */
1419                 if (stats[ST_F_THROTTLE].type)
1420                         chunk_appendf(out, "<td class=ac>%d %%</td>\n", stats[ST_F_THROTTLE].u.u32);
1421                 else
1422                         chunk_appendf(out, "<td class=ac>-</td>");
1423
1424                 if (flags & STAT_SHMODULES) {
1425                         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
1426                                 chunk_appendf(out, "<td>");
1427
1428                                 if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV) {
1429                                         chunk_appendf(out,
1430                                                       "<u>%s<div class=tips><table class=det>",
1431                                                       mod->name);
1432                                         for (j = 0; j < mod->stats_count; ++j) {
1433                                                 chunk_appendf(out,
1434                                                               "<tr><th>%s</th><td>%s</td></tr>",
1435                                                               mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
1436                                                 ++i;
1437                                         }
1438                                         chunk_appendf(out, "</table></div></u>");
1439                                 } else {
1440                                         i += mod->stats_count;
1441                                 }
1442
1443                                 chunk_appendf(out, "</td>");
1444                         }
1445                 }
1446
1447                 chunk_appendf(out, "</tr>\n");
1448         }
1449         else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE) {
1450                 chunk_appendf(out, "<tr class=\"backend\">");
1451                 if (flags & STAT_ADMIN) {
1452                         /* Column sub-heading for Enable or Disable server */
1453                         chunk_appendf(out, "<td></td>");
1454                 }
1455                 chunk_appendf(out,
1456                               "<td class=ac>"
1457                               /* name */
1458                               "%s<a name=\"%s/Backend\"></a>"
1459                               "<a class=lfsb href=\"#%s/Backend\">Backend</a>"
1460                               "",
1461                               (flags & STAT_SHLGNDS)?"<u>":"",
1462                               field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_PXNAME));
1463
1464                 if (flags & STAT_SHLGNDS) {
1465                         /* balancing */
1466                         chunk_appendf(out, "<div class=tips>balancing: %s",
1467                                       field_str(stats, ST_F_ALGO));
1468
1469                         /* cookie */
1470                         if (stats[ST_F_COOKIE].type) {
1471                                 chunk_appendf(out, ", cookie: '");
1472                                 chunk_initstr(&src, field_str(stats, ST_F_COOKIE));
1473                                 chunk_htmlencode(out, &src);
1474                                 chunk_appendf(out, "'");
1475                         }
1476                         chunk_appendf(out, "</div>");
1477                 }
1478
1479                 chunk_appendf(out,
1480                               "%s</td>"
1481                               /* queue : current, max */
1482                               "<td>%s</td><td>%s</td><td></td>"
1483                               /* sessions rate : current, max, limit */
1484                               "<td>%s</td><td>%s</td><td></td>"
1485                               "",
1486                               (flags & STAT_SHLGNDS)?"</u>":"",
1487                               U2H(stats[ST_F_QCUR].u.u32), U2H(stats[ST_F_QMAX].u.u32),
1488                               U2H(stats[ST_F_RATE].u.u32), U2H(stats[ST_F_RATE_MAX].u.u32));
1489
1490                 chunk_appendf(out,
1491                               /* sessions: current, max, limit, total */
1492                               "<td>%s</td><td>%s</td><td>%s</td>"
1493                               "<td><u>%s<div class=tips><table class=det>"
1494                               "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
1495                               "",
1496                               U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
1497                               U2H(stats[ST_F_STOT].u.u64),
1498                               U2H(stats[ST_F_STOT].u.u64));
1499
1500                 /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1501                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
1502                         chunk_appendf(out,
1503                                       "<tr><th>New connections:</th><td>%s</td></tr>"
1504                                       "<tr><th>Reused connections:</th><td>%s</td><td>(%d%%)</td></tr>"
1505                                       "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
1506                                       "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
1507                                       "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
1508                                       "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
1509                                       "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
1510                                       "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
1511                                       "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
1512                                       "<tr><th>- other responses:</th><td>%s</td></tr>"
1513                                       "<tr><th>Cache lookups:</th><td>%s</td></tr>"
1514                                       "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
1515                                       "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
1516                                       "<tr><th>Internal errors:</th><td>%s</td></tr>"
1517                                       "",
1518                                       U2H(stats[ST_F_CONNECT].u.u64),
1519                                       U2H(stats[ST_F_REUSE].u.u64),
1520                                       (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64) ?
1521                                       (int)(100 * stats[ST_F_REUSE].u.u64 / (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64)) : 0,
1522                                       U2H(stats[ST_F_REQ_TOT].u.u64),
1523                                       U2H(stats[ST_F_HRSP_1XX].u.u64),
1524                                       U2H(stats[ST_F_HRSP_2XX].u.u64),
1525                                       U2H(stats[ST_F_COMP_RSP].u.u64),
1526                                       stats[ST_F_HRSP_2XX].u.u64 ?
1527                                       (int)(100 * stats[ST_F_COMP_RSP].u.u64 / stats[ST_F_HRSP_2XX].u.u64) : 0,
1528                                       U2H(stats[ST_F_HRSP_3XX].u.u64),
1529                                       U2H(stats[ST_F_HRSP_4XX].u.u64),
1530                                       U2H(stats[ST_F_HRSP_5XX].u.u64),
1531                                       U2H(stats[ST_F_HRSP_OTHER].u.u64),
1532                                       U2H(stats[ST_F_CACHE_LOOKUPS].u.u64),
1533                                       U2H(stats[ST_F_CACHE_HITS].u.u64),
1534                                       stats[ST_F_CACHE_LOOKUPS].u.u64 ?
1535                                       (int)(100 * stats[ST_F_CACHE_HITS].u.u64 / stats[ST_F_CACHE_LOOKUPS].u.u64) : 0,
1536                                       U2H(stats[ST_F_WREW].u.u64),
1537                                       U2H(stats[ST_F_EINT].u.u64));
1538                 }
1539
1540                 chunk_appendf(out, "<tr><th colspan=3>Max / Avg over last 1024 success. conn.</th></tr>");
1541                 chunk_appendf(out, "<tr><th>- Queue time:</th><td>%s / %s</td><td>ms</td></tr>",
1542                               U2H(stats[ST_F_QT_MAX].u.u32), U2H(stats[ST_F_QTIME].u.u32));
1543                 chunk_appendf(out, "<tr><th>- Connect time:</th><td>%s / %s</td><td>ms</td></tr>",
1544                               U2H(stats[ST_F_CT_MAX].u.u32), U2H(stats[ST_F_CTIME].u.u32));
1545                 if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
1546                         chunk_appendf(out, "<tr><th>- Responses time:</th><td>%s / %s</td><td>ms</td></tr>",
1547                                       U2H(stats[ST_F_RT_MAX].u.u32), U2H(stats[ST_F_RTIME].u.u32));
1548                 chunk_appendf(out, "<tr><th>- Total time:</th><td>%s / %s</td><td>ms</td></tr>",
1549                               U2H(stats[ST_F_TT_MAX].u.u32), U2H(stats[ST_F_TTIME].u.u32));
1550
1551                 chunk_appendf(out,
1552                               "</table></div></u></td>"
1553                               /* sessions: lbtot, last */
1554                               "<td>%s</td><td>%s</td>"
1555                               /* bytes: in */
1556                               "<td>%s</td>"
1557                               "",
1558                               U2H(stats[ST_F_LBTOT].u.u64),
1559                               human_time(stats[ST_F_LASTSESS].u.s32, 1),
1560                               U2H(stats[ST_F_BIN].u.u64));
1561
1562                 chunk_appendf(out,
1563                               /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
1564                               "<td>%s%s<div class=tips><table class=det>"
1565                               "<tr><th>Response bytes in:</th><td>%s</td></tr>"
1566                               "<tr><th>Compression in:</th><td>%s</td></tr>"
1567                               "<tr><th>Compression out:</th><td>%s</td><td>(%d%%)</td></tr>"
1568                               "<tr><th>Compression bypass:</th><td>%s</td></tr>"
1569                               "<tr><th>Total bytes saved:</th><td>%s</td><td>(%d%%)</td></tr>"
1570                               "</table></div>%s</td>",
1571                               (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "<u>":"",
1572                               U2H(stats[ST_F_BOUT].u.u64),
1573                               U2H(stats[ST_F_BOUT].u.u64),
1574                               U2H(stats[ST_F_COMP_IN].u.u64),
1575                               U2H(stats[ST_F_COMP_OUT].u.u64),
1576                               stats[ST_F_COMP_IN].u.u64 ? (int)(stats[ST_F_COMP_OUT].u.u64 * 100 / stats[ST_F_COMP_IN].u.u64) : 0,
1577                               U2H(stats[ST_F_COMP_BYP].u.u64),
1578                               U2H(stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64),
1579                               stats[ST_F_BOUT].u.u64 ? (int)((stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64) * 100 / stats[ST_F_BOUT].u.u64) : 0,
1580                               (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "</u>":"");
1581
1582                 chunk_appendf(out,
1583                               /* denied: req, resp */
1584                               "<td>%s</td><td>%s</td>"
1585                               /* errors : request, connect */
1586                               "<td></td><td>%s</td>"
1587                               /* errors : response */
1588                               "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
1589                               /* warnings: retries, redispatches */
1590                               "<td>%lld</td><td>%lld</td>"
1591                               /* backend status: reflect backend status (up/down): we display UP
1592                                * if the backend has known working servers or if it has no server at
1593                                * all (eg: for stats). Then we display the total weight, number of
1594                                * active and backups. */
1595                               "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d/%d</td>"
1596                               "<td class=ac>%d</td><td class=ac>%d</td>"
1597                               "",
1598                               U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
1599                               U2H(stats[ST_F_ECON].u.u64),
1600                               U2H(stats[ST_F_ERESP].u.u64),
1601                               (long long)stats[ST_F_CLI_ABRT].u.u64,
1602                               (long long)stats[ST_F_SRV_ABRT].u.u64,
1603                               (long long)stats[ST_F_WRETR].u.u64, (long long)stats[ST_F_WREDIS].u.u64,
1604                               human_time(stats[ST_F_LASTCHG].u.u32, 1),
1605                               strcmp(field_str(stats, ST_F_STATUS), "DOWN") ? field_str(stats, ST_F_STATUS) : "<font color=\"red\"><b>DOWN</b></font>",
1606                               stats[ST_F_WEIGHT].u.u32, stats[ST_F_UWEIGHT].u.u32,
1607                               stats[ST_F_ACT].u.u32, stats[ST_F_BCK].u.u32);
1608
1609                 chunk_appendf(out,
1610                               /* rest of backend: nothing, down transitions, total downtime, throttle */
1611                               "<td class=ac>&nbsp;</td><td>%d</td>"
1612                               "<td>%s</td>"
1613                               "<td></td>",
1614                               stats[ST_F_CHKDOWN].u.u32,
1615                               stats[ST_F_DOWNTIME].type ? human_time(stats[ST_F_DOWNTIME].u.u32, 1) : "&nbsp;");
1616
1617                 if (flags & STAT_SHMODULES) {
1618                         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
1619                                 chunk_appendf(out, "<td>");
1620
1621                                 if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE) {
1622                                         chunk_appendf(out,
1623                                                       "<u>%s<div class=tips><table class=det>",
1624                                                       mod->name);
1625                                         for (j = 0; j < mod->stats_count; ++j) {
1626                                                 chunk_appendf(out,
1627                                                               "<tr><th>%s</th><td>%s</td></tr>",
1628                                                               mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
1629                                                 ++i;
1630                                         }
1631                                         chunk_appendf(out, "</table></div></u>");
1632                                 } else {
1633                                         i += mod->stats_count;
1634                                 }
1635
1636                                 chunk_appendf(out, "</td>");
1637                         }
1638                 }
1639
1640                 chunk_appendf(out, "</tr>");
1641         }
1642
1643         return 1;
1644 }
1645
1646 int stats_dump_one_line(const struct field *stats, size_t stats_count,
1647                         struct appctx *appctx)
1648 {
1649         struct show_stat_ctx *ctx = appctx->svcctx;
1650         int ret;
1651
1652         if (ctx->flags & STAT_FMT_HTML)
1653                 ret = stats_dump_fields_html(&trash_chunk, stats, ctx);
1654         else if (ctx->flags & STAT_FMT_TYPED)
1655                 ret = stats_dump_fields_typed(&trash_chunk, stats, stats_count, ctx);
1656         else if (ctx->flags & STAT_FMT_JSON)
1657                 ret = stats_dump_fields_json(&trash_chunk, stats, stats_count, ctx);
1658         else
1659                 ret = stats_dump_fields_csv(&trash_chunk, stats, stats_count, ctx);
1660
1661         if (ret)
1662                 ctx->flags |= STAT_STARTED;
1663
1664         return ret;
1665 }
1666
1667 /* Fill <stats> with the frontend statistics. <stats> is preallocated array of
1668  * length <len>. If <selected_field> is != NULL, only fill this one. The length
1669  * of the array must be at least ST_F_TOTAL_FIELDS. If this length is less than
1670  * this value, or if the selected field is not implemented for frontends, the
1671  * function returns 0, otherwise, it returns 1.
1672  */
1673 int stats_fill_fe_stats(struct proxy *px, struct field *stats, int len,
1674                         enum stat_field *selected_field)
1675 {
1676         enum stat_field current_field = (selected_field != NULL ? *selected_field : 0);
1677
1678         if (len < ST_F_TOTAL_FIELDS)
1679                 return 0;
1680
1681         for (; current_field < ST_F_TOTAL_FIELDS; current_field++) {
1682                 struct field metric = { 0 };
1683
1684                 switch (current_field) {
1685                         case ST_F_PXNAME:
1686                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
1687                                 break;
1688                         case ST_F_SVNAME:
1689                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, "FRONTEND");
1690                                 break;
1691                         case ST_F_MODE:
1692                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode));
1693                                 break;
1694                         case ST_F_SCUR:
1695                                 metric = mkf_u32(0, px->feconn);
1696                                 break;
1697                         case ST_F_SMAX:
1698                                 metric = mkf_u32(FN_MAX, px->fe_counters.conn_max);
1699                                 break;
1700                         case ST_F_SLIM:
1701                                 metric = mkf_u32(FO_CONFIG|FN_LIMIT, px->maxconn);
1702                                 break;
1703                         case ST_F_STOT:
1704                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.cum_sess);
1705                                 break;
1706                         case ST_F_BIN:
1707                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.bytes_in);
1708                                 break;
1709                         case ST_F_BOUT:
1710                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.bytes_out);
1711                                 break;
1712                         case ST_F_DREQ:
1713                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.denied_req);
1714                                 break;
1715                         case ST_F_DRESP:
1716                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.denied_resp);
1717                                 break;
1718                         case ST_F_EREQ:
1719                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.failed_req);
1720                                 break;
1721                         case ST_F_DCON:
1722                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.denied_conn);
1723                                 break;
1724                         case ST_F_DSES:
1725                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.denied_sess);
1726                                 break;
1727                         case ST_F_STATUS: {
1728                                 const char *state;
1729
1730                                 if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED))
1731                                         state = "STOP";
1732                                 else if (px->flags & PR_FL_PAUSED)
1733                                         state = "PAUSED";
1734                                 else
1735                                         state = "OPEN";
1736                                 metric = mkf_str(FO_STATUS, state);
1737                                 break;
1738                         }
1739                         case ST_F_PID:
1740                                 metric = mkf_u32(FO_KEY, 1);
1741                                 break;
1742                         case ST_F_IID:
1743                                 metric = mkf_u32(FO_KEY|FS_SERVICE, px->uuid);
1744                                 break;
1745                         case ST_F_SID:
1746                                 metric = mkf_u32(FO_KEY|FS_SERVICE, 0);
1747                                 break;
1748                         case ST_F_TYPE:
1749                                 metric = mkf_u32(FO_CONFIG|FS_SERVICE, STATS_TYPE_FE);
1750                                 break;
1751                         case ST_F_RATE:
1752                                 metric = mkf_u32(FN_RATE, read_freq_ctr(&px->fe_sess_per_sec));
1753                                 break;
1754                         case ST_F_RATE_LIM:
1755                                 metric = mkf_u32(FO_CONFIG|FN_LIMIT, px->fe_sps_lim);
1756                                 break;
1757                         case ST_F_RATE_MAX:
1758                                 metric = mkf_u32(FN_MAX, px->fe_counters.sps_max);
1759                                 break;
1760                         case ST_F_WREW:
1761                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.failed_rewrites);
1762                                 break;
1763                         case ST_F_EINT:
1764                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.internal_errors);
1765                                 break;
1766                         case ST_F_HRSP_1XX:
1767                                 if (px->mode == PR_MODE_HTTP)
1768                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.rsp[1]);
1769                                 break;
1770                         case ST_F_HRSP_2XX:
1771                                 if (px->mode == PR_MODE_HTTP)
1772                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.rsp[2]);
1773                                 break;
1774                         case ST_F_HRSP_3XX:
1775                                 if (px->mode == PR_MODE_HTTP)
1776                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.rsp[3]);
1777                                 break;
1778                         case ST_F_HRSP_4XX:
1779                                 if (px->mode == PR_MODE_HTTP)
1780                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.rsp[4]);
1781                                 break;
1782                         case ST_F_HRSP_5XX:
1783                                 if (px->mode == PR_MODE_HTTP)
1784                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.rsp[5]);
1785                                 break;
1786                         case ST_F_HRSP_OTHER:
1787                                 if (px->mode == PR_MODE_HTTP)
1788                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.rsp[0]);
1789                                 break;
1790                         case ST_F_INTERCEPTED:
1791                                 if (px->mode == PR_MODE_HTTP)
1792                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.intercepted_req);
1793                                 break;
1794                         case ST_F_CACHE_LOOKUPS:
1795                                 if (px->mode == PR_MODE_HTTP)
1796                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.cache_lookups);
1797                                 break;
1798                         case ST_F_CACHE_HITS:
1799                                 if (px->mode == PR_MODE_HTTP)
1800                                         metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.cache_hits);
1801                                 break;
1802                         case ST_F_REQ_RATE:
1803                                 metric = mkf_u32(FN_RATE, read_freq_ctr(&px->fe_req_per_sec));
1804                                 break;
1805                         case ST_F_REQ_RATE_MAX:
1806                                 metric = mkf_u32(FN_MAX, px->fe_counters.p.http.rps_max);
1807                                 break;
1808                         case ST_F_REQ_TOT:
1809                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.cum_req);
1810                                 break;
1811                         case ST_F_COMP_IN:
1812                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.comp_in);
1813                                 break;
1814                         case ST_F_COMP_OUT:
1815                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.comp_out);
1816                                 break;
1817                         case ST_F_COMP_BYP:
1818                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.comp_byp);
1819                                 break;
1820                         case ST_F_COMP_RSP:
1821                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.p.http.comp_rsp);
1822                                 break;
1823                         case ST_F_CONN_RATE:
1824                                 metric = mkf_u32(FN_RATE, read_freq_ctr(&px->fe_conn_per_sec));
1825                                 break;
1826                         case ST_F_CONN_RATE_MAX:
1827                                 metric = mkf_u32(FN_MAX, px->fe_counters.cps_max);
1828                                 break;
1829                         case ST_F_CONN_TOT:
1830                                 metric = mkf_u64(FN_COUNTER, px->fe_counters.cum_conn);
1831                                 break;
1832                         default:
1833                                 /* not used for frontends. If a specific metric
1834                                  * is requested, return an error. Otherwise continue.
1835                                  */
1836                                 if (selected_field != NULL)
1837                                         return 0;
1838                                 continue;
1839                 }
1840                 stats[current_field] = metric;
1841                 if (selected_field != NULL)
1842                         break;
1843         }
1844         return 1;
1845 }
1846
1847 /* Dumps a frontend's line to the local trash buffer for the current proxy <px>
1848  * and uses the state from stream connector <sc>. The caller is responsible for
1849  * clearing the local trash buffer if needed. Returns non-zero if it emits
1850  * anything, zero otherwise.
1851  */
1852 static int stats_dump_fe_stats(struct stconn *sc, struct proxy *px)
1853 {
1854         struct appctx *appctx = __sc_appctx(sc);
1855         struct show_stat_ctx *ctx = appctx->svcctx;
1856         struct field *stats = stat_l[STATS_DOMAIN_PROXY];
1857         struct stats_module *mod;
1858         size_t stats_count = ST_F_TOTAL_FIELDS;
1859
1860         if (!(px->cap & PR_CAP_FE))
1861                 return 0;
1862
1863         if ((ctx->flags & STAT_BOUND) && !(ctx->type & (1 << STATS_TYPE_FE)))
1864                 return 0;
1865
1866         memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
1867
1868         if (!stats_fill_fe_stats(px, stats, ST_F_TOTAL_FIELDS, NULL))
1869                 return 0;
1870
1871         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
1872                 void *counters;
1873
1874                 if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE)) {
1875                         stats_count += mod->stats_count;
1876                         continue;
1877                 }
1878
1879                 counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, mod);
1880                 mod->fill_stats(counters, stats + stats_count);
1881                 stats_count += mod->stats_count;
1882         }
1883
1884         return stats_dump_one_line(stats, stats_count, appctx);
1885 }
1886
1887 /* Fill <stats> with the listener statistics. <stats> is preallocated array of
1888  * length <len>. The length of the array must be at least ST_F_TOTAL_FIELDS. If
1889  * this length is less then this value, the function returns 0, otherwise, it
1890  * returns 1.  If selected_field is != NULL, only fill this one. <flags> can
1891  * take the value STAT_SHLGNDS.
1892  */
1893 int stats_fill_li_stats(struct proxy *px, struct listener *l, int flags,
1894                         struct field *stats, int len, enum stat_field *selected_field)
1895 {
1896         enum stat_field current_field = (selected_field != NULL ? *selected_field : 0);
1897         struct buffer *out = get_trash_chunk();
1898
1899         if (len < ST_F_TOTAL_FIELDS)
1900                 return 0;
1901
1902         if (!l->counters)
1903                 return 0;
1904
1905         chunk_reset(out);
1906
1907         for (; current_field < ST_F_TOTAL_FIELDS; current_field++) {
1908                 struct field metric = { 0 };
1909
1910                 switch (current_field) {
1911                         case ST_F_PXNAME:
1912                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
1913                                 break;
1914                         case ST_F_SVNAME:
1915                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, l->name);
1916                                 break;
1917                         case ST_F_MODE:
1918                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode));
1919                                 break;
1920                         case ST_F_SCUR:
1921                                 metric = mkf_u32(0, l->nbconn);
1922                                 break;
1923                         case ST_F_SMAX:
1924                                 metric = mkf_u32(FN_MAX, l->counters->conn_max);
1925                                 break;
1926                         case ST_F_SLIM:
1927                                 metric = mkf_u32(FO_CONFIG|FN_LIMIT, l->maxconn);
1928                                 break;
1929                         case ST_F_STOT:
1930                                 metric = mkf_u64(FN_COUNTER, l->counters->cum_conn);
1931                                 break;
1932                         case ST_F_BIN:
1933                                 metric = mkf_u64(FN_COUNTER, l->counters->bytes_in);
1934                                 break;
1935                         case ST_F_BOUT:
1936                                 metric = mkf_u64(FN_COUNTER, l->counters->bytes_out);
1937                                 break;
1938                         case ST_F_DREQ:
1939                                 metric = mkf_u64(FN_COUNTER, l->counters->denied_req);
1940                                 break;
1941                         case ST_F_DRESP:
1942                                 metric = mkf_u64(FN_COUNTER, l->counters->denied_resp);
1943                                 break;
1944                         case ST_F_EREQ:
1945                                 metric = mkf_u64(FN_COUNTER, l->counters->failed_req);
1946                                 break;
1947                         case ST_F_DCON:
1948                                 metric = mkf_u64(FN_COUNTER, l->counters->denied_conn);
1949                                 break;
1950                         case ST_F_DSES:
1951                                 metric = mkf_u64(FN_COUNTER, l->counters->denied_sess);
1952                                 break;
1953                         case ST_F_STATUS:
1954                                 metric = mkf_str(FO_STATUS, li_status_st[get_li_status(l)]);
1955                                 break;
1956                         case ST_F_PID:
1957                                 metric = mkf_u32(FO_KEY, 1);
1958                                 break;
1959                         case ST_F_IID:
1960                                 metric = mkf_u32(FO_KEY|FS_SERVICE, px->uuid);
1961                                 break;
1962                         case ST_F_SID:
1963                                 metric = mkf_u32(FO_KEY|FS_SERVICE, l->luid);
1964                                 break;
1965                         case ST_F_TYPE:
1966                                 metric = mkf_u32(FO_CONFIG|FS_SERVICE, STATS_TYPE_SO);
1967                                 break;
1968                         case ST_F_WREW:
1969                                 metric = mkf_u64(FN_COUNTER, l->counters->failed_rewrites);
1970                                 break;
1971                         case ST_F_EINT:
1972                                 metric = mkf_u64(FN_COUNTER, l->counters->internal_errors);
1973                                 break;
1974                         case ST_F_ADDR:
1975                                 if (flags & STAT_SHLGNDS) {
1976                                         char str[INET6_ADDRSTRLEN];
1977                                         int port;
1978
1979                                         port = get_host_port(&l->rx.addr);
1980                                         switch (addr_to_str(&l->rx.addr, str, sizeof(str))) {
1981                                         case AF_INET:
1982                                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out));
1983                                                 chunk_appendf(out, "%s:%d", str, port);
1984                                                 break;
1985                                         case AF_INET6:
1986                                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out));
1987                                                 chunk_appendf(out, "[%s]:%d", str, port);
1988                                                 break;
1989                                         case AF_UNIX:
1990                                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, "unix");
1991                                                 break;
1992                                         case -1:
1993                                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out));
1994                                                 chunk_strcat(out, strerror(errno));
1995                                                 break;
1996                                         default: /* address family not supported */
1997                                                 break;
1998                                         }
1999                                 }
2000                                 break;
2001                         default:
2002                                 /* not used for listen. If a specific metric
2003                                  * is requested, return an error. Otherwise continue.
2004                                  */
2005                                 if (selected_field != NULL)
2006                                         return 0;
2007                                 continue;
2008                 }
2009                 stats[current_field] = metric;
2010                 if (selected_field != NULL)
2011                         break;
2012         }
2013         return 1;
2014 }
2015
2016 /* Dumps a line for listener <l> and proxy <px> to the local trash buffer and
2017  * uses the state from stream connector <sc>. The caller is responsible for
2018  * clearing the local trash buffer if needed. Returns non-zero if it emits
2019  * anything, zero otherwise.
2020  */
2021 static int stats_dump_li_stats(struct stconn *sc, struct proxy *px, struct listener *l)
2022 {
2023         struct appctx *appctx = __sc_appctx(sc);
2024         struct show_stat_ctx *ctx = appctx->svcctx;
2025         struct field *stats = stat_l[STATS_DOMAIN_PROXY];
2026         struct stats_module *mod;
2027         size_t stats_count = ST_F_TOTAL_FIELDS;
2028
2029         memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
2030
2031         if (!stats_fill_li_stats(px, l, ctx->flags, stats,
2032                                  ST_F_TOTAL_FIELDS, NULL))
2033                 return 0;
2034
2035         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
2036                 void *counters;
2037
2038                 if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI)) {
2039                         stats_count += mod->stats_count;
2040                         continue;
2041                 }
2042
2043                 counters = EXTRA_COUNTERS_GET(l->extra_counters, mod);
2044                 mod->fill_stats(counters, stats + stats_count);
2045                 stats_count += mod->stats_count;
2046         }
2047
2048         return stats_dump_one_line(stats, stats_count, appctx);
2049 }
2050
2051 enum srv_stats_state {
2052         SRV_STATS_STATE_DOWN = 0,
2053         SRV_STATS_STATE_DOWN_AGENT,
2054         SRV_STATS_STATE_GOING_UP,
2055         SRV_STATS_STATE_UP_GOING_DOWN,
2056         SRV_STATS_STATE_UP,
2057         SRV_STATS_STATE_NOLB_GOING_DOWN,
2058         SRV_STATS_STATE_NOLB,
2059         SRV_STATS_STATE_DRAIN_GOING_DOWN,
2060         SRV_STATS_STATE_DRAIN,
2061         SRV_STATS_STATE_DRAIN_AGENT,
2062         SRV_STATS_STATE_NO_CHECK,
2063
2064         SRV_STATS_STATE_COUNT, /* Must be last */
2065 };
2066
2067 static const char *srv_hlt_st[SRV_STATS_STATE_COUNT] = {
2068         [SRV_STATS_STATE_DOWN]                  = "DOWN",
2069         [SRV_STATS_STATE_DOWN_AGENT]            = "DOWN (agent)",
2070         [SRV_STATS_STATE_GOING_UP]              = "DOWN %d/%d",
2071         [SRV_STATS_STATE_UP_GOING_DOWN]         = "UP %d/%d",
2072         [SRV_STATS_STATE_UP]                    = "UP",
2073         [SRV_STATS_STATE_NOLB_GOING_DOWN]       = "NOLB %d/%d",
2074         [SRV_STATS_STATE_NOLB]                  = "NOLB",
2075         [SRV_STATS_STATE_DRAIN_GOING_DOWN]      = "DRAIN %d/%d",
2076         [SRV_STATS_STATE_DRAIN]                 = "DRAIN",
2077         [SRV_STATS_STATE_DRAIN_AGENT]           = "DRAIN (agent)",
2078         [SRV_STATS_STATE_NO_CHECK]              = "no check"
2079 };
2080
2081 /* Compute server state helper
2082  */
2083 static void stats_fill_sv_stats_computestate(struct server *sv, struct server *ref,
2084                                              enum srv_stats_state *state)
2085 {
2086         if (sv->cur_state == SRV_ST_RUNNING || sv->cur_state == SRV_ST_STARTING) {
2087                 if ((ref->check.state & CHK_ST_ENABLED) &&
2088                     (ref->check.health < ref->check.rise + ref->check.fall - 1)) {
2089                         *state = SRV_STATS_STATE_UP_GOING_DOWN;
2090                 } else {
2091                         *state = SRV_STATS_STATE_UP;
2092                 }
2093
2094                 if (sv->cur_admin & SRV_ADMF_DRAIN) {
2095                         if (ref->agent.state & CHK_ST_ENABLED)
2096                                 *state = SRV_STATS_STATE_DRAIN_AGENT;
2097                         else if (*state == SRV_STATS_STATE_UP_GOING_DOWN)
2098                                 *state = SRV_STATS_STATE_DRAIN_GOING_DOWN;
2099                         else
2100                                 *state = SRV_STATS_STATE_DRAIN;
2101                 }
2102
2103                 if (*state == SRV_STATS_STATE_UP && !(ref->check.state & CHK_ST_ENABLED)) {
2104                         *state = SRV_STATS_STATE_NO_CHECK;
2105                 }
2106         }
2107         else if (sv->cur_state == SRV_ST_STOPPING) {
2108                 if ((!(sv->check.state & CHK_ST_ENABLED) && !sv->track) ||
2109                     (ref->check.health == ref->check.rise + ref->check.fall - 1)) {
2110                         *state = SRV_STATS_STATE_NOLB;
2111                 } else {
2112                         *state = SRV_STATS_STATE_NOLB_GOING_DOWN;
2113                 }
2114         }
2115         else {  /* stopped */
2116                 if ((ref->agent.state & CHK_ST_ENABLED) && !ref->agent.health) {
2117                         *state = SRV_STATS_STATE_DOWN_AGENT;
2118                 } else if ((ref->check.state & CHK_ST_ENABLED) && !ref->check.health) {
2119                         *state = SRV_STATS_STATE_DOWN; /* DOWN */
2120                 } else if ((ref->agent.state & CHK_ST_ENABLED) || (ref->check.state & CHK_ST_ENABLED)) {
2121                         *state = SRV_STATS_STATE_GOING_UP;
2122                 } else {
2123                         *state = SRV_STATS_STATE_DOWN; /* DOWN, unchecked */
2124                 }
2125         }
2126 }
2127
2128 /* Fill <stats> with the backend statistics. <stats> is preallocated array of
2129  * length <len>. If <selected_field> is != NULL, only fill this one. The length
2130  * of the array must be at least ST_F_TOTAL_FIELDS. If this length is less than
2131  * this value, or if the selected field is not implemented for servers, the
2132  * function returns 0, otherwise, it returns 1. <flags> can take the value
2133  * STAT_SHLGNDS.
2134  */
2135 int stats_fill_sv_stats(struct proxy *px, struct server *sv, int flags,
2136                         struct field *stats, int len,
2137                         enum stat_field *selected_field)
2138 {
2139         enum stat_field current_field = (selected_field != NULL ? *selected_field : 0);
2140         struct server *via = sv->track ? sv->track : sv;
2141         struct server *ref = via;
2142         enum srv_stats_state state = 0;
2143         char str[INET6_ADDRSTRLEN];
2144         struct buffer *out = get_trash_chunk();
2145         char *fld_status;
2146         long long srv_samples_counter;
2147         unsigned int srv_samples_window = TIME_STATS_SAMPLES;
2148
2149         if (len < ST_F_TOTAL_FIELDS)
2150                 return 0;
2151
2152         chunk_reset(out);
2153
2154         /* compute state for later use */
2155         if (selected_field == NULL || *selected_field == ST_F_STATUS ||
2156             *selected_field == ST_F_CHECK_RISE || *selected_field == ST_F_CHECK_FALL ||
2157             *selected_field == ST_F_CHECK_HEALTH || *selected_field == ST_F_HANAFAIL) {
2158                 /* we have "via" which is the tracked server as described in the configuration,
2159                  * and "ref" which is the checked server and the end of the chain.
2160                  */
2161                 while (ref->track)
2162                         ref = ref->track;
2163                 stats_fill_sv_stats_computestate(sv, ref, &state);
2164         }
2165
2166         /* compue time values for later use */
2167         if (selected_field == NULL || *selected_field == ST_F_QTIME ||
2168             *selected_field == ST_F_CTIME || *selected_field == ST_F_RTIME ||
2169             *selected_field == ST_F_TTIME) {
2170                 srv_samples_counter = (px->mode == PR_MODE_HTTP) ? sv->counters.p.http.cum_req : sv->counters.cum_lbconn;
2171                 if (srv_samples_counter < TIME_STATS_SAMPLES && srv_samples_counter > 0)
2172                         srv_samples_window = srv_samples_counter;
2173         }
2174
2175         for (; current_field < ST_F_TOTAL_FIELDS; current_field++) {
2176                 struct field metric = { 0 };
2177
2178                 switch (current_field) {
2179                         case ST_F_PXNAME:
2180                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
2181                                 break;
2182                         case ST_F_SVNAME:
2183                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, sv->id);
2184                                 break;
2185                         case ST_F_MODE:
2186                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode));
2187                                 break;
2188                         case ST_F_QCUR:
2189                                 metric = mkf_u32(0, sv->queue.length);
2190                                 break;
2191                         case ST_F_QMAX:
2192                                 metric = mkf_u32(FN_MAX, sv->counters.nbpend_max);
2193                                 break;
2194                         case ST_F_SCUR:
2195                                 metric = mkf_u32(0, sv->cur_sess);
2196                                 break;
2197                         case ST_F_SMAX:
2198                                 metric = mkf_u32(FN_MAX, sv->counters.cur_sess_max);
2199                                 break;
2200                         case ST_F_SLIM:
2201                                 if (sv->maxconn)
2202                                         metric = mkf_u32(FO_CONFIG|FN_LIMIT, sv->maxconn);
2203                                 break;
2204                         case ST_F_SRV_ICUR:
2205                                 metric = mkf_u32(0, sv->curr_idle_conns);
2206                                 break;
2207                         case ST_F_SRV_ILIM:
2208                                 if (sv->max_idle_conns != -1)
2209                                         metric = mkf_u32(FO_CONFIG|FN_LIMIT, sv->max_idle_conns);
2210                                 break;
2211                         case ST_F_STOT:
2212                                 metric = mkf_u64(FN_COUNTER, sv->counters.cum_sess);
2213                                 break;
2214                         case ST_F_BIN:
2215                                 metric = mkf_u64(FN_COUNTER, sv->counters.bytes_in);
2216                                 break;
2217                         case ST_F_BOUT:
2218                                 metric = mkf_u64(FN_COUNTER, sv->counters.bytes_out);
2219                                 break;
2220                         case ST_F_DRESP:
2221                                 metric = mkf_u64(FN_COUNTER, sv->counters.denied_resp);
2222                                 break;
2223                         case ST_F_ECON:
2224                                 metric = mkf_u64(FN_COUNTER, sv->counters.failed_conns);
2225                                 break;
2226                         case ST_F_ERESP:
2227                                 metric = mkf_u64(FN_COUNTER, sv->counters.failed_resp);
2228                                 break;
2229                         case ST_F_WRETR:
2230                                 metric = mkf_u64(FN_COUNTER, sv->counters.retries);
2231                                 break;
2232                         case ST_F_WREDIS:
2233                                 metric = mkf_u64(FN_COUNTER, sv->counters.redispatches);
2234                                 break;
2235                         case ST_F_WREW:
2236                                 metric = mkf_u64(FN_COUNTER, sv->counters.failed_rewrites);
2237                                 break;
2238                         case ST_F_EINT:
2239                                 metric = mkf_u64(FN_COUNTER, sv->counters.internal_errors);
2240                                 break;
2241                         case ST_F_CONNECT:
2242                                 metric = mkf_u64(FN_COUNTER, sv->counters.connect);
2243                                 break;
2244                         case ST_F_REUSE:
2245                                 metric = mkf_u64(FN_COUNTER, sv->counters.reuse);
2246                                 break;
2247                         case ST_F_IDLE_CONN_CUR:
2248                                 metric = mkf_u32(0, sv->curr_idle_nb);
2249                                 break;
2250                         case ST_F_SAFE_CONN_CUR:
2251                                 metric = mkf_u32(0, sv->curr_safe_nb);
2252                                 break;
2253                         case ST_F_USED_CONN_CUR:
2254                                 metric = mkf_u32(0, sv->curr_used_conns);
2255                                 break;
2256                         case ST_F_NEED_CONN_EST:
2257                                 metric = mkf_u32(0, sv->est_need_conns);
2258                                 break;
2259                         case ST_F_STATUS:
2260                                 fld_status = chunk_newstr(out);
2261                                 if (sv->cur_admin & SRV_ADMF_RMAINT)
2262                                         chunk_appendf(out, "MAINT (resolution)");
2263                                 else if (sv->cur_admin & SRV_ADMF_IMAINT)
2264                                         chunk_appendf(out, "MAINT (via %s/%s)", via->proxy->id, via->id);
2265                                 else if (sv->cur_admin & SRV_ADMF_MAINT)
2266                                         chunk_appendf(out, "MAINT");
2267                                 else
2268                                         chunk_appendf(out,
2269                                                       srv_hlt_st[state],
2270                                                       (ref->cur_state != SRV_ST_STOPPED) ? (ref->check.health - ref->check.rise + 1) : (ref->check.health),
2271                                                       (ref->cur_state != SRV_ST_STOPPED) ? (ref->check.fall) : (ref->check.rise));
2272
2273                                 metric = mkf_str(FO_STATUS, fld_status);
2274                                 break;
2275                         case ST_F_LASTCHG:
2276                                 metric = mkf_u32(FN_AGE, now.tv_sec - sv->last_change);
2277                                 break;
2278                         case ST_F_WEIGHT:
2279                                 metric = mkf_u32(FN_AVG, (sv->cur_eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv);
2280                                 break;
2281                         case ST_F_UWEIGHT:
2282                                 metric = mkf_u32(FN_AVG, sv->uweight);
2283                                 break;
2284                         case ST_F_ACT:
2285                                 metric = mkf_u32(FO_STATUS, (sv->flags & SRV_F_BACKUP) ? 0 : 1);
2286                                 break;
2287                         case ST_F_BCK:
2288                                 metric = mkf_u32(FO_STATUS, (sv->flags & SRV_F_BACKUP) ? 1 : 0);
2289                                 break;
2290                         case ST_F_CHKFAIL:
2291                                 if (sv->check.state & CHK_ST_ENABLED)
2292                                         metric = mkf_u64(FN_COUNTER, sv->counters.failed_checks);
2293                                 break;
2294                         case ST_F_CHKDOWN:
2295                                 if (sv->check.state & CHK_ST_ENABLED)
2296                                         metric = mkf_u64(FN_COUNTER, sv->counters.down_trans);
2297                                 break;
2298                         case ST_F_DOWNTIME:
2299                                 if (sv->check.state & CHK_ST_ENABLED)
2300                                         metric = mkf_u32(FN_COUNTER, srv_downtime(sv));
2301                                 break;
2302                         case ST_F_QLIMIT:
2303                                 if (sv->maxqueue)
2304                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, sv->maxqueue);
2305                                 break;
2306                         case ST_F_PID:
2307                                 metric = mkf_u32(FO_KEY, 1);
2308                                 break;
2309                         case ST_F_IID:
2310                                 metric = mkf_u32(FO_KEY|FS_SERVICE, px->uuid);
2311                                 break;
2312                         case ST_F_SID:
2313                                 metric = mkf_u32(FO_KEY|FS_SERVICE, sv->puid);
2314                                 break;
2315                         case ST_F_SRID:
2316                                 metric = mkf_u32(FN_COUNTER, sv->rid);
2317                                 break;
2318                         case ST_F_THROTTLE:
2319                                 if (sv->cur_state == SRV_ST_STARTING && !server_is_draining(sv))
2320                                         metric = mkf_u32(FN_AVG, server_throttle_rate(sv));
2321                                 break;
2322                         case ST_F_LBTOT:
2323                                 metric = mkf_u64(FN_COUNTER, sv->counters.cum_lbconn);
2324                                 break;
2325                         case ST_F_TRACKED:
2326                                 if (sv->track) {
2327                                         char *fld_track = chunk_newstr(out);
2328                                         chunk_appendf(out, "%s/%s", sv->track->proxy->id, sv->track->id);
2329                                         metric = mkf_str(FO_CONFIG|FN_NAME|FS_SERVICE, fld_track);
2330                                 }
2331                                 break;
2332                         case ST_F_TYPE:
2333                                 metric = mkf_u32(FO_CONFIG|FS_SERVICE, STATS_TYPE_SV);
2334                                 break;
2335                         case ST_F_RATE:
2336                                 metric = mkf_u32(FN_RATE, read_freq_ctr(&sv->sess_per_sec));
2337                                 break;
2338                         case ST_F_RATE_MAX:
2339                                 metric = mkf_u32(FN_MAX, sv->counters.sps_max);
2340                                 break;
2341                         case ST_F_CHECK_STATUS:
2342                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) {
2343                                         const char *fld_chksts;
2344
2345                                         fld_chksts = chunk_newstr(out);
2346                                         chunk_strcat(out, "* "); // for check in progress
2347                                         chunk_strcat(out, get_check_status_info(sv->check.status));
2348                                         if (!(sv->check.state & CHK_ST_INPROGRESS))
2349                                                 fld_chksts += 2; // skip "* "
2350                                         metric = mkf_str(FN_OUTPUT, fld_chksts);
2351                                 }
2352                                 break;
2353                         case ST_F_CHECK_CODE:
2354                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED &&
2355                                         sv->check.status >= HCHK_STATUS_L57DATA)
2356                                         metric = mkf_u32(FN_OUTPUT, sv->check.code);
2357                                 break;
2358                         case ST_F_CHECK_DURATION:
2359                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED &&
2360                                         sv->check.status >= HCHK_STATUS_CHECKED)
2361                                         metric = mkf_u64(FN_DURATION, MAX(sv->check.duration, 0));
2362                                 break;
2363                         case ST_F_CHECK_DESC:
2364                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2365                                         metric = mkf_str(FN_OUTPUT, get_check_status_description(sv->check.status));
2366                                 break;
2367                         case ST_F_LAST_CHK:
2368                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2369                                         metric = mkf_str(FN_OUTPUT, sv->check.desc);
2370                                 break;
2371                         case ST_F_CHECK_RISE:
2372                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2373                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, ref->check.rise);
2374                                 break;
2375                         case ST_F_CHECK_FALL:
2376                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2377                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, ref->check.fall);
2378                                 break;
2379                         case ST_F_CHECK_HEALTH:
2380                                 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2381                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, ref->check.health);
2382                                 break;
2383                         case ST_F_AGENT_STATUS:
2384                                 if  ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) {
2385                                         const char *fld_chksts;
2386
2387                                         fld_chksts = chunk_newstr(out);
2388                                         chunk_strcat(out, "* "); // for check in progress
2389                                         chunk_strcat(out, get_check_status_info(sv->agent.status));
2390                                         if (!(sv->agent.state & CHK_ST_INPROGRESS))
2391                                                 fld_chksts += 2; // skip "* "
2392                                         metric = mkf_str(FN_OUTPUT, fld_chksts);
2393                                 }
2394                                 break;
2395                         case ST_F_AGENT_CODE:
2396                                 if  ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED &&
2397                                      (sv->agent.status >= HCHK_STATUS_L57DATA))
2398                                         metric = mkf_u32(FN_OUTPUT, sv->agent.code);
2399                                 break;
2400                         case ST_F_AGENT_DURATION:
2401                                 if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2402                                         metric = mkf_u64(FN_DURATION, sv->agent.duration);
2403                                 break;
2404                         case ST_F_AGENT_DESC:
2405                                 if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2406                                         metric = mkf_str(FN_OUTPUT, get_check_status_description(sv->agent.status));
2407                                 break;
2408                         case ST_F_LAST_AGT:
2409                                 if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2410                                         metric = mkf_str(FN_OUTPUT, sv->agent.desc);
2411                                 break;
2412                         case ST_F_AGENT_RISE:
2413                                 if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2414                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, sv->agent.rise);
2415                                 break;
2416                         case ST_F_AGENT_FALL:
2417                                 if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2418                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, sv->agent.fall);
2419                                 break;
2420                         case ST_F_AGENT_HEALTH:
2421                                 if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED)
2422                                         metric = mkf_u32(FO_CONFIG|FS_SERVICE, sv->agent.health);
2423                                 break;
2424                         case ST_F_REQ_TOT:
2425                                 if (px->mode == PR_MODE_HTTP)
2426                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.cum_req);
2427                                 break;
2428                         case ST_F_HRSP_1XX:
2429                                 if (px->mode == PR_MODE_HTTP)
2430                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.rsp[1]);
2431                                 break;
2432                         case ST_F_HRSP_2XX:
2433                                 if (px->mode == PR_MODE_HTTP)
2434                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.rsp[2]);
2435                                 break;
2436                         case ST_F_HRSP_3XX:
2437                                 if (px->mode == PR_MODE_HTTP)
2438                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.rsp[3]);
2439                                 break;
2440                         case ST_F_HRSP_4XX:
2441                                 if (px->mode == PR_MODE_HTTP)
2442                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.rsp[4]);
2443                                 break;
2444                         case ST_F_HRSP_5XX:
2445                                 if (px->mode == PR_MODE_HTTP)
2446                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.rsp[5]);
2447                                 break;
2448                         case ST_F_HRSP_OTHER:
2449                                 if (px->mode == PR_MODE_HTTP)
2450                                         metric = mkf_u64(FN_COUNTER, sv->counters.p.http.rsp[0]);
2451                                 break;
2452                         case ST_F_HANAFAIL:
2453                                 if (ref->observe)
2454                                         metric = mkf_u64(FN_COUNTER, sv->counters.failed_hana);
2455                                 break;
2456                         case ST_F_CLI_ABRT:
2457                                 metric = mkf_u64(FN_COUNTER, sv->counters.cli_aborts);
2458                                 break;
2459                         case ST_F_SRV_ABRT:
2460                                 metric = mkf_u64(FN_COUNTER, sv->counters.srv_aborts);
2461                                 break;
2462                         case ST_F_LASTSESS:
2463                                 metric = mkf_s32(FN_AGE, srv_lastsession(sv));
2464                                 break;
2465                         case ST_F_QTIME:
2466                                 metric = mkf_u32(FN_AVG, swrate_avg(sv->counters.q_time, srv_samples_window));
2467                                 break;
2468                         case ST_F_CTIME:
2469                                 metric = mkf_u32(FN_AVG, swrate_avg(sv->counters.c_time, srv_samples_window));
2470                                 break;
2471                         case ST_F_RTIME:
2472                                 metric = mkf_u32(FN_AVG, swrate_avg(sv->counters.d_time, srv_samples_window));
2473                                 break;
2474                         case ST_F_TTIME:
2475                                 metric = mkf_u32(FN_AVG, swrate_avg(sv->counters.t_time, srv_samples_window));
2476                                 break;
2477                         case ST_F_QT_MAX:
2478                                 metric = mkf_u32(FN_MAX, sv->counters.qtime_max);
2479                                 break;
2480                         case ST_F_CT_MAX:
2481                                 metric = mkf_u32(FN_MAX, sv->counters.ctime_max);
2482                                 break;
2483                         case ST_F_RT_MAX:
2484                                 metric = mkf_u32(FN_MAX, sv->counters.dtime_max);
2485                                 break;
2486                         case ST_F_TT_MAX:
2487                                 metric = mkf_u32(FN_MAX, sv->counters.ttime_max);
2488                                 break;
2489                         case ST_F_ADDR:
2490                                 if (flags & STAT_SHLGNDS) {
2491                                         switch (addr_to_str(&sv->addr, str, sizeof(str))) {
2492                                                 case AF_INET:
2493                                                         metric = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out));
2494                                                         chunk_appendf(out, "%s:%d", str, sv->svc_port);
2495                                                         break;
2496                                                 case AF_INET6:
2497                                                         metric = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out));
2498                                                         chunk_appendf(out, "[%s]:%d", str, sv->svc_port);
2499                                                         break;
2500                                                 case AF_UNIX:
2501                                                         metric = mkf_str(FO_CONFIG|FS_SERVICE, "unix");
2502                                                         break;
2503                                                 case -1:
2504                                                         metric = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out));
2505                                                         chunk_strcat(out, strerror(errno));
2506                                                         break;
2507                                                 default: /* address family not supported */
2508                                                         break;
2509                                         }
2510                                 }
2511                                 break;
2512                         case ST_F_COOKIE:
2513                                 if (flags & STAT_SHLGNDS && sv->cookie)
2514                                         metric = mkf_str(FO_CONFIG|FN_NAME|FS_SERVICE, sv->cookie);
2515                                 break;
2516                         default:
2517                                 /* not used for servers. If a specific metric
2518                                  * is requested, return an error. Otherwise continue.
2519                                  */
2520                                 if (selected_field != NULL)
2521                                         return 0;
2522                                 continue;
2523                 }
2524                 stats[current_field] = metric;
2525                 if (selected_field != NULL)
2526                         break;
2527         }
2528         return 1;
2529 }
2530
2531 /* Dumps a line for server <sv> and proxy <px> to the local trash vbuffer and
2532  * uses the state from stream connector <sc>, and server state <state>. The
2533  * caller is responsible for clearing the local trash buffer if needed. Returns
2534  * non-zero if it emits anything, zero otherwise.
2535  */
2536 static int stats_dump_sv_stats(struct stconn *sc, struct proxy *px, struct server *sv)
2537 {
2538         struct appctx *appctx = __sc_appctx(sc);
2539         struct show_stat_ctx *ctx = appctx->svcctx;
2540         struct stats_module *mod;
2541         struct field *stats = stat_l[STATS_DOMAIN_PROXY];
2542         size_t stats_count = ST_F_TOTAL_FIELDS;
2543
2544         memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
2545
2546         if (!stats_fill_sv_stats(px, sv, ctx->flags, stats,
2547                                  ST_F_TOTAL_FIELDS, NULL))
2548                 return 0;
2549
2550         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
2551                 void *counters;
2552
2553                 if (stats_get_domain(mod->domain_flags) != STATS_DOMAIN_PROXY)
2554                         continue;
2555
2556                 if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV)) {
2557                         stats_count += mod->stats_count;
2558                         continue;
2559                 }
2560
2561                 counters = EXTRA_COUNTERS_GET(sv->extra_counters, mod);
2562                 mod->fill_stats(counters, stats + stats_count);
2563                 stats_count += mod->stats_count;
2564         }
2565
2566         return stats_dump_one_line(stats, stats_count, appctx);
2567 }
2568
2569 /* Helper to compute srv values for a given backend
2570  */
2571 static void stats_fill_be_stats_computesrv(struct proxy *px, int *nbup, int *nbsrv, int *totuw)
2572 {
2573         int nbup_tmp, nbsrv_tmp, totuw_tmp;
2574         const struct server *srv;
2575
2576         nbup_tmp = nbsrv_tmp = totuw_tmp = 0;
2577         for (srv = px->srv; srv; srv = srv->next) {
2578                 if (srv->cur_state != SRV_ST_STOPPED) {
2579                         nbup_tmp++;
2580                         if (srv_currently_usable(srv) &&
2581                             (!px->srv_act ^ !(srv->flags & SRV_F_BACKUP)))
2582                                 totuw_tmp += srv->uweight;
2583                 }
2584                 nbsrv_tmp++;
2585         }
2586
2587         HA_RWLOCK_RDLOCK(LBPRM_LOCK, &px->lbprm.lock);
2588         if (!px->srv_act && px->lbprm.fbck)
2589                 totuw_tmp = px->lbprm.fbck->uweight;
2590         HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &px->lbprm.lock);
2591
2592         /* use tmp variable then assign result to make gcc happy */
2593         *nbup = nbup_tmp;
2594         *nbsrv = nbsrv_tmp;
2595         *totuw = totuw_tmp;
2596 }
2597
2598 /* Fill <stats> with the backend statistics. <stats> is preallocated array of
2599  * length <len>. If <selected_field> is != NULL, only fill this one. The length
2600  * of the array must be at least ST_F_TOTAL_FIELDS. If this length is less than
2601  * this value, or if the selected field is not implemented for backends, the
2602  * function returns 0, otherwise, it returns 1. <flags> can take the value
2603  * STAT_SHLGNDS.
2604  */
2605 int stats_fill_be_stats(struct proxy *px, int flags, struct field *stats, int len,
2606                         enum stat_field *selected_field)
2607 {
2608         enum stat_field current_field = (selected_field != NULL ? *selected_field : 0);
2609         long long be_samples_counter;
2610         unsigned int be_samples_window = TIME_STATS_SAMPLES;
2611         struct buffer *out = get_trash_chunk();
2612         int nbup, nbsrv, totuw;
2613         char *fld;
2614
2615         if (len < ST_F_TOTAL_FIELDS)
2616                 return 0;
2617
2618         nbup = nbsrv = totuw = 0;
2619         /* some srv values compute for later if we either select all fields or
2620          * need them for one of the mentioned ones */
2621         if (selected_field == NULL || *selected_field == ST_F_STATUS ||
2622             *selected_field == ST_F_UWEIGHT)
2623                 stats_fill_be_stats_computesrv(px, &nbup, &nbsrv, &totuw);
2624
2625         /* same here but specific to time fields */
2626         if (selected_field == NULL || *selected_field == ST_F_QTIME ||
2627             *selected_field == ST_F_CTIME || *selected_field == ST_F_RTIME ||
2628             *selected_field == ST_F_TTIME) {
2629                 be_samples_counter = (px->mode == PR_MODE_HTTP) ? px->be_counters.p.http.cum_req : px->be_counters.cum_lbconn;
2630                 if (be_samples_counter < TIME_STATS_SAMPLES && be_samples_counter > 0)
2631                         be_samples_window = be_samples_counter;
2632         }
2633
2634         for (; current_field < ST_F_TOTAL_FIELDS; current_field++) {
2635                 struct field metric = { 0 };
2636
2637                 switch (current_field) {
2638                         case ST_F_PXNAME:
2639                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
2640                                 break;
2641                         case ST_F_SVNAME:
2642                                 metric = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, "BACKEND");
2643                                 break;
2644                         case ST_F_MODE:
2645                                 metric = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode));
2646                                 break;
2647                         case ST_F_QCUR:
2648                                 metric = mkf_u32(0, px->queue.length);
2649                                 break;
2650                         case ST_F_QMAX:
2651                                 metric = mkf_u32(FN_MAX, px->be_counters.nbpend_max);
2652                                 break;
2653                         case ST_F_SCUR:
2654                                 metric = mkf_u32(0, px->beconn);
2655                                 break;
2656                         case ST_F_SMAX:
2657                                 metric = mkf_u32(FN_MAX, px->be_counters.conn_max);
2658                                 break;
2659                         case ST_F_SLIM:
2660                                 metric = mkf_u32(FO_CONFIG|FN_LIMIT, px->fullconn);
2661                                 break;
2662                         case ST_F_STOT:
2663                                 metric = mkf_u64(FN_COUNTER, px->be_counters.cum_conn);
2664                                 break;
2665                         case ST_F_BIN:
2666                                 metric = mkf_u64(FN_COUNTER, px->be_counters.bytes_in);
2667                                 break;
2668                         case ST_F_BOUT:
2669                                 metric = mkf_u64(FN_COUNTER, px->be_counters.bytes_out);
2670                                 break;
2671                         case ST_F_DREQ:
2672                                 metric = mkf_u64(FN_COUNTER, px->be_counters.denied_req);
2673                                 break;
2674                         case ST_F_DRESP:
2675                                 metric = mkf_u64(FN_COUNTER, px->be_counters.denied_resp);
2676                                 break;
2677                         case ST_F_ECON:
2678                                 metric = mkf_u64(FN_COUNTER, px->be_counters.failed_conns);
2679                                 break;
2680                         case ST_F_ERESP:
2681                                 metric = mkf_u64(FN_COUNTER, px->be_counters.failed_resp);
2682                                 break;
2683                         case ST_F_WRETR:
2684                                 metric = mkf_u64(FN_COUNTER, px->be_counters.retries);
2685                                 break;
2686                         case ST_F_WREDIS:
2687                                 metric = mkf_u64(FN_COUNTER, px->be_counters.redispatches);
2688                                 break;
2689                         case ST_F_WREW:
2690                                 metric = mkf_u64(FN_COUNTER, px->be_counters.failed_rewrites);
2691                                 break;
2692                         case ST_F_EINT:
2693                                 metric = mkf_u64(FN_COUNTER, px->be_counters.internal_errors);
2694                                 break;
2695                         case ST_F_CONNECT:
2696                                 metric = mkf_u64(FN_COUNTER, px->be_counters.connect);
2697                                 break;
2698                         case ST_F_REUSE:
2699                                 metric = mkf_u64(FN_COUNTER, px->be_counters.reuse);
2700                                 break;
2701                         case ST_F_STATUS:
2702                                 fld = chunk_newstr(out);
2703                                 chunk_appendf(out, "%s", (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN");
2704                                 if (flags & (STAT_HIDE_MAINT|STAT_HIDE_DOWN))
2705                                         chunk_appendf(out, " (%d/%d)", nbup, nbsrv);
2706                                 metric = mkf_str(FO_STATUS, fld);
2707                                 break;
2708                         case ST_F_AGG_SRV_CHECK_STATUS:   // DEPRECATED
2709                         case ST_F_AGG_SRV_STATUS:
2710                                 metric = mkf_u32(FN_GAUGE, 0);
2711                                 break;
2712                         case ST_F_AGG_CHECK_STATUS:
2713                                 metric = mkf_u32(FN_GAUGE, 0);
2714                                 break;
2715                         case ST_F_WEIGHT:
2716                                 metric = mkf_u32(FN_AVG, (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv);
2717                                 break;
2718                         case ST_F_UWEIGHT:
2719                                 metric = mkf_u32(FN_AVG, totuw);
2720                                 break;
2721                         case ST_F_ACT:
2722                                 metric = mkf_u32(0, px->srv_act);
2723                                 break;
2724                         case ST_F_BCK:
2725                                 metric = mkf_u32(0, px->srv_bck);
2726                                 break;
2727                         case ST_F_CHKDOWN:
2728                                 metric = mkf_u64(FN_COUNTER, px->down_trans);
2729                                 break;
2730                         case ST_F_LASTCHG:
2731                                 metric = mkf_u32(FN_AGE, now.tv_sec - px->last_change);
2732                                 break;
2733                         case ST_F_DOWNTIME:
2734                                 if (px->srv)
2735                                         metric = mkf_u32(FN_COUNTER, be_downtime(px));
2736                                 break;
2737                         case ST_F_PID:
2738                                 metric = mkf_u32(FO_KEY, 1);
2739                                 break;
2740                         case ST_F_IID:
2741                                 metric = mkf_u32(FO_KEY|FS_SERVICE, px->uuid);
2742                                 break;
2743                         case ST_F_SID:
2744                                 metric = mkf_u32(FO_KEY|FS_SERVICE, 0);
2745                                 break;
2746                         case ST_F_LBTOT:
2747                                 metric = mkf_u64(FN_COUNTER, px->be_counters.cum_lbconn);
2748                                 break;
2749                         case ST_F_TYPE:
2750                                 metric = mkf_u32(FO_CONFIG|FS_SERVICE, STATS_TYPE_BE);
2751                                 break;
2752                         case ST_F_RATE:
2753                                 metric = mkf_u32(0, read_freq_ctr(&px->be_sess_per_sec));
2754                                 break;
2755                         case ST_F_RATE_MAX:
2756                                 metric = mkf_u32(0, px->be_counters.sps_max);
2757                                 break;
2758                         case ST_F_COOKIE:
2759                                 if (flags & STAT_SHLGNDS && px->cookie_name)
2760                                         metric = mkf_str(FO_CONFIG|FN_NAME|FS_SERVICE, px->cookie_name);
2761                                 break;
2762                         case ST_F_ALGO:
2763                                 if (flags & STAT_SHLGNDS)
2764                                         metric = mkf_str(FO_CONFIG|FS_SERVICE, backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
2765                                 break;
2766                         case ST_F_REQ_TOT:
2767                                 if (px->mode == PR_MODE_HTTP)
2768                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.cum_req);
2769                                 break;
2770                         case ST_F_HRSP_1XX:
2771                                 if (px->mode == PR_MODE_HTTP)
2772                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.rsp[1]);
2773                                 break;
2774                         case ST_F_HRSP_2XX:
2775                                 if (px->mode == PR_MODE_HTTP)
2776                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.rsp[2]);
2777                                 break;
2778                         case ST_F_HRSP_3XX:
2779                                 if (px->mode == PR_MODE_HTTP)
2780                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.rsp[3]);
2781                                 break;
2782                         case ST_F_HRSP_4XX:
2783                                 if (px->mode == PR_MODE_HTTP)
2784                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.rsp[4]);
2785                                 break;
2786                         case ST_F_HRSP_5XX:
2787                                 if (px->mode == PR_MODE_HTTP)
2788                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.rsp[5]);
2789                                 break;
2790                         case ST_F_HRSP_OTHER:
2791                                 if (px->mode == PR_MODE_HTTP)
2792                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.rsp[0]);
2793                                 break;
2794                         case ST_F_CACHE_LOOKUPS:
2795                                 if (px->mode == PR_MODE_HTTP)
2796                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.cache_lookups);
2797                                 break;
2798                         case ST_F_CACHE_HITS:
2799                                 if (px->mode == PR_MODE_HTTP)
2800                                         metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.cache_hits);
2801                                 break;
2802                         case ST_F_CLI_ABRT:
2803                                 metric = mkf_u64(FN_COUNTER, px->be_counters.cli_aborts);
2804                                 break;
2805                         case ST_F_SRV_ABRT:
2806                                 metric = mkf_u64(FN_COUNTER, px->be_counters.srv_aborts);
2807                                 break;
2808                         case ST_F_COMP_IN:
2809                                 metric = mkf_u64(FN_COUNTER, px->be_counters.comp_in);
2810                                 break;
2811                         case ST_F_COMP_OUT:
2812                                 metric = mkf_u64(FN_COUNTER, px->be_counters.comp_out);
2813                                 break;
2814                         case ST_F_COMP_BYP:
2815                                 metric = mkf_u64(FN_COUNTER, px->be_counters.comp_byp);
2816                                 break;
2817                         case ST_F_COMP_RSP:
2818                                 metric = mkf_u64(FN_COUNTER, px->be_counters.p.http.comp_rsp);
2819                                 break;
2820                         case ST_F_LASTSESS:
2821                                 metric = mkf_s32(FN_AGE, be_lastsession(px));
2822                                 break;
2823                         case ST_F_QTIME:
2824                                 metric = mkf_u32(FN_AVG, swrate_avg(px->be_counters.q_time, be_samples_window));
2825                                 break;
2826                         case ST_F_CTIME:
2827                                 metric = mkf_u32(FN_AVG, swrate_avg(px->be_counters.c_time, be_samples_window));
2828                                 break;
2829                         case ST_F_RTIME:
2830                                 metric = mkf_u32(FN_AVG, swrate_avg(px->be_counters.d_time, be_samples_window));
2831                                 break;
2832                         case ST_F_TTIME:
2833                                 metric = mkf_u32(FN_AVG, swrate_avg(px->be_counters.t_time, be_samples_window));
2834                                 break;
2835                         case ST_F_QT_MAX:
2836                                 metric = mkf_u32(FN_MAX, px->be_counters.qtime_max);
2837                                 break;
2838                         case ST_F_CT_MAX:
2839                                 metric = mkf_u32(FN_MAX, px->be_counters.ctime_max);
2840                                 break;
2841                         case ST_F_RT_MAX:
2842                                 metric = mkf_u32(FN_MAX, px->be_counters.dtime_max);
2843                                 break;
2844                         case ST_F_TT_MAX:
2845                                 metric = mkf_u32(FN_MAX, px->be_counters.ttime_max);
2846                                 break;
2847                         default:
2848                                 /* not used for backends. If a specific metric
2849                                  * is requested, return an error. Otherwise continue.
2850                                  */
2851                                 if (selected_field != NULL)
2852                                         return 0;
2853                                 continue;
2854                 }
2855                 stats[current_field] = metric;
2856                 if (selected_field != NULL)
2857                         break;
2858         }
2859         return 1;
2860 }
2861
2862 /* Dumps a line for backend <px> to the local trash buffer for and uses the
2863  * state from stream interface <si>. The caller is responsible for clearing the
2864  * local trash buffer if needed.  Returns non-zero if it emits anything, zero
2865  * otherwise.
2866  */
2867 static int stats_dump_be_stats(struct stconn *sc, struct proxy *px)
2868 {
2869         struct appctx *appctx = __sc_appctx(sc);
2870         struct show_stat_ctx *ctx = appctx->svcctx;
2871         struct field *stats = stat_l[STATS_DOMAIN_PROXY];
2872         struct stats_module *mod;
2873         size_t stats_count = ST_F_TOTAL_FIELDS;
2874
2875         if (!(px->cap & PR_CAP_BE))
2876                 return 0;
2877
2878         if ((ctx->flags & STAT_BOUND) && !(ctx->type & (1 << STATS_TYPE_BE)))
2879                 return 0;
2880
2881         memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
2882
2883         if (!stats_fill_be_stats(px, ctx->flags, stats, ST_F_TOTAL_FIELDS, NULL))
2884                 return 0;
2885
2886         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
2887                 struct extra_counters *counters;
2888
2889                 if (stats_get_domain(mod->domain_flags) != STATS_DOMAIN_PROXY)
2890                         continue;
2891
2892                 if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE)) {
2893                         stats_count += mod->stats_count;
2894                         continue;
2895                 }
2896
2897                 counters = EXTRA_COUNTERS_GET(px->extra_counters_be, mod);
2898                 mod->fill_stats(counters, stats + stats_count);
2899                 stats_count += mod->stats_count;
2900         }
2901
2902         return stats_dump_one_line(stats, stats_count, appctx);
2903 }
2904
2905 /* Dumps the HTML table header for proxy <px> to the local trash buffer for and
2906  * uses the state from stream connector <sc> and per-uri parameters <uri>. The
2907  * caller is responsible for clearing the local trash buffer if needed.
2908  */
2909 static void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px)
2910 {
2911         struct appctx *appctx = __sc_appctx(sc);
2912         struct show_stat_ctx *ctx = appctx->svcctx;
2913         char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
2914         struct stats_module *mod;
2915         int stats_module_len = 0;
2916
2917         if (px->cap & PR_CAP_BE && px->srv && (ctx->flags & STAT_ADMIN)) {
2918                 /* A form to enable/disable this proxy servers */
2919
2920                 /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
2921                 scope_txt[0] = 0;
2922                 if (ctx->scope_len) {
2923                         const char *scope_ptr = stats_scope_ptr(appctx, sc);
2924
2925                         strcpy(scope_txt, STAT_SCOPE_PATTERN);
2926                         memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
2927                         scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
2928                 }
2929
2930                 chunk_appendf(&trash_chunk,
2931                               "<form method=\"post\">");
2932         }
2933
2934         /* print a new table */
2935         chunk_appendf(&trash_chunk,
2936                       "<table class=\"tbl\" width=\"100%%\">\n"
2937                       "<tr class=\"titre\">"
2938                       "<th class=\"pxname\" width=\"10%%\">");
2939
2940         chunk_appendf(&trash_chunk,
2941                       "<a name=\"%s\"></a>%s"
2942                       "<a class=px href=\"#%s\">%s</a>",
2943                       px->id,
2944                       (ctx->flags & STAT_SHLGNDS) ? "<u>":"",
2945                       px->id, px->id);
2946
2947         if (ctx->flags & STAT_SHLGNDS) {
2948                 /* cap, mode, id */
2949                 chunk_appendf(&trash_chunk, "<div class=tips>cap: %s, mode: %s, id: %d",
2950                               proxy_cap_str(px->cap), proxy_mode_str(px->mode),
2951                               px->uuid);
2952                 chunk_appendf(&trash_chunk, "</div>");
2953         }
2954
2955         chunk_appendf(&trash_chunk,
2956                       "%s</th>"
2957                       "<th class=\"%s\" width=\"90%%\">%s</th>"
2958                       "</tr>\n"
2959                       "</table>\n"
2960                       "<table class=\"tbl\" width=\"100%%\">\n"
2961                       "<tr class=\"titre\">",
2962                       (ctx->flags & STAT_SHLGNDS) ? "</u>":"",
2963                       px->desc ? "desc" : "empty", px->desc ? px->desc : "");
2964
2965         if (ctx->flags & STAT_ADMIN) {
2966                 /* Column heading for Enable or Disable server */
2967                 if ((px->cap & PR_CAP_BE) && px->srv)
2968                         chunk_appendf(&trash_chunk,
2969                                       "<th rowspan=2 width=1><input type=\"checkbox\" "
2970                                       "onclick=\"for(c in document.getElementsByClassName('%s-checkbox')) "
2971                                       "document.getElementsByClassName('%s-checkbox').item(c).checked = this.checked\"></th>",
2972                                       px->id,
2973                                       px->id);
2974                 else
2975                         chunk_appendf(&trash_chunk, "<th rowspan=2></th>");
2976         }
2977
2978         chunk_appendf(&trash_chunk,
2979                       "<th rowspan=2></th>"
2980                       "<th colspan=3>Queue</th>"
2981                       "<th colspan=3>Session rate</th><th colspan=6>Sessions</th>"
2982                       "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
2983                       "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
2984                       "<th colspan=9>Server</th>");
2985
2986         if (ctx->flags & STAT_SHMODULES) {
2987                 // calculate the count of module for colspan attribute
2988                 list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
2989                         ++stats_module_len;
2990                 }
2991                 chunk_appendf(&trash_chunk, "<th colspan=%d>Extra modules</th>",
2992                               stats_module_len);
2993         }
2994
2995         chunk_appendf(&trash_chunk,
2996                       "</tr>\n"
2997                       "<tr class=\"titre\">"
2998                       "<th>Cur</th><th>Max</th><th>Limit</th>"
2999                       "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
3000                       "<th>Limit</th><th>Total</th><th>LbTot</th><th>Last</th><th>In</th><th>Out</th>"
3001                       "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
3002                       "<th>Resp</th><th>Retr</th><th>Redis</th>"
3003                       "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
3004                       "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
3005                       "<th>Thrtle</th>\n");
3006
3007         if (ctx->flags & STAT_SHMODULES) {
3008                 list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
3009                         chunk_appendf(&trash_chunk, "<th>%s</th>", mod->name);
3010                 }
3011         }
3012
3013         chunk_appendf(&trash_chunk, "</tr>");
3014 }
3015
3016 /* Dumps the HTML table trailer for proxy <px> to the local trash buffer for and
3017  * uses the state from stream connector <sc>. The caller is responsible for
3018  * clearing the local trash buffer if needed.
3019  */
3020 static void stats_dump_html_px_end(struct stconn *sc, struct proxy *px)
3021 {
3022         struct appctx *appctx = __sc_appctx(sc);
3023         struct show_stat_ctx *ctx = appctx->svcctx;
3024
3025         chunk_appendf(&trash_chunk, "</table>");
3026
3027         if ((px->cap & PR_CAP_BE) && px->srv && (ctx->flags & STAT_ADMIN)) {
3028                 /* close the form used to enable/disable this proxy servers */
3029                 chunk_appendf(&trash_chunk,
3030                               "Choose the action to perform on the checked servers : "
3031                               "<select name=action>"
3032                               "<option value=\"\"></option>"
3033                               "<option value=\"ready\">Set state to READY</option>"
3034                               "<option value=\"drain\">Set state to DRAIN</option>"
3035                               "<option value=\"maint\">Set state to MAINT</option>"
3036                               "<option value=\"dhlth\">Health: disable checks</option>"
3037                               "<option value=\"ehlth\">Health: enable checks</option>"
3038                               "<option value=\"hrunn\">Health: force UP</option>"
3039                               "<option value=\"hnolb\">Health: force NOLB</option>"
3040                               "<option value=\"hdown\">Health: force DOWN</option>"
3041                               "<option value=\"dagent\">Agent: disable checks</option>"
3042                               "<option value=\"eagent\">Agent: enable checks</option>"
3043                               "<option value=\"arunn\">Agent: force UP</option>"
3044                               "<option value=\"adown\">Agent: force DOWN</option>"
3045                               "<option value=\"shutdown\">Kill Sessions</option>"
3046                               "</select>"
3047                               "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
3048                               "&nbsp;<input type=\"submit\" value=\"Apply\">"
3049                               "</form>",
3050                               px->uuid);
3051         }
3052
3053         chunk_appendf(&trash_chunk, "<p>\n");
3054 }
3055
3056 /*
3057  * Dumps statistics for a proxy. The output is sent to the stream connector's
3058  * input buffer. Returns 0 if it had to stop dumping data because of lack of
3059  * buffer space, or non-zero if everything completed. This function is used
3060  * both by the CLI and the HTTP entry points, and is able to dump the output
3061  * in HTML or CSV formats. If the later, <uri> must be NULL.
3062  */
3063 int stats_dump_proxy_to_buffer(struct stconn *sc, struct htx *htx,
3064                                struct proxy *px, struct uri_auth *uri)
3065 {
3066         struct appctx *appctx = __sc_appctx(sc);
3067         struct show_stat_ctx *ctx = appctx->svcctx;
3068         struct stream *s = __sc_strm(sc);
3069         struct channel *rep = sc_ic(sc);
3070         struct server *sv, *svs;        /* server and server-state, server-state=server or server->track */
3071         struct listener *l;
3072         int current_field;
3073
3074         chunk_reset(&trash_chunk);
3075 more:
3076         current_field = ctx->field;
3077
3078         switch (ctx->px_st) {
3079         case STAT_PX_ST_INIT:
3080                 /* we are on a new proxy */
3081                 if (uri && uri->scope) {
3082                         /* we have a limited scope, we have to check the proxy name */
3083                         struct stat_scope *scope;
3084                         int len;
3085
3086                         len = strlen(px->id);
3087                         scope = uri->scope;
3088
3089                         while (scope) {
3090                                 /* match exact proxy name */
3091                                 if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
3092                                         break;
3093
3094                                 /* match '.' which means 'self' proxy */
3095                                 if (strcmp(scope->px_id, ".") == 0 && px == s->be)
3096                                         break;
3097                                 scope = scope->next;
3098                         }
3099
3100                         /* proxy name not found : don't dump anything */
3101                         if (scope == NULL)
3102                                 return 1;
3103                 }
3104
3105                 /* if the user has requested a limited output and the proxy
3106                  * name does not match, skip it.
3107                  */
3108                 if (ctx->scope_len) {
3109                         const char *scope_ptr = stats_scope_ptr(appctx, sc);
3110
3111                         if (strnistr(px->id, strlen(px->id), scope_ptr, ctx->scope_len) == NULL)
3112                                 return 1;
3113                 }
3114
3115                 if ((ctx->flags & STAT_BOUND) &&
3116                     (ctx->iid != -1) &&
3117                     (px->uuid != ctx->iid))
3118                         return 1;
3119
3120                 ctx->px_st = STAT_PX_ST_TH;
3121                 __fallthrough;
3122
3123         case STAT_PX_ST_TH:
3124                 if (ctx->flags & STAT_FMT_HTML) {
3125                         stats_dump_html_px_hdr(sc, px);
3126                         if (!stats_putchk(rep, htx, &trash_chunk))
3127                                 goto full;
3128                 }
3129
3130                 ctx->px_st = STAT_PX_ST_FE;
3131                 __fallthrough;
3132
3133         case STAT_PX_ST_FE:
3134                 /* print the frontend */
3135                 if (stats_dump_fe_stats(sc, px)) {
3136                         if (!stats_putchk(rep, htx, &trash_chunk))
3137                                 goto full;
3138                         if (ctx->field)
3139                                 goto more;
3140                 }
3141
3142                 current_field = 0;
3143                 ctx->obj2 = px->conf.listeners.n;
3144                 ctx->px_st = STAT_PX_ST_LI;
3145                 __fallthrough;
3146
3147         case STAT_PX_ST_LI:
3148                 /* obj2 points to listeners list as initialized above */
3149                 for (; ctx->obj2 != &px->conf.listeners; ctx->obj2 = l->by_fe.n) {
3150                         if (htx) {
3151                                 if (htx_almost_full(htx))
3152                                         goto full;
3153                         }
3154                         else {
3155                                 if (buffer_almost_full(&rep->buf))
3156                                         goto full;
3157                         }
3158
3159                         l = LIST_ELEM(ctx->obj2, struct listener *, by_fe);
3160                         if (!l->counters)
3161                                 continue;
3162
3163                         if (ctx->flags & STAT_BOUND) {
3164                                 if (!(ctx->type & (1 << STATS_TYPE_SO)))
3165                                         break;
3166
3167                                 if (ctx->sid != -1 && l->luid != ctx->sid)
3168                                         continue;
3169                         }
3170
3171                         /* print the frontend */
3172                         if (stats_dump_li_stats(sc, px, l)) {
3173                                 if (!stats_putchk(rep, htx, &trash_chunk))
3174                                         goto full;
3175                                 if (ctx->field)
3176                                         goto more;
3177                         }
3178                 }
3179
3180                 current_field = 0;
3181                 ctx->obj2 = px->srv; /* may be NULL */
3182                 ctx->px_st = STAT_PX_ST_SV;
3183                 __fallthrough;
3184
3185         case STAT_PX_ST_SV:
3186                 /* obj2 points to servers list as initialized above.
3187                  *
3188                  * A server may be removed during the stats dumping.
3189                  * Temporarily increment its refcount to prevent its
3190                  * anticipated cleaning. Call free_server to release it.
3191                  */
3192                 for (; ctx->obj2 != NULL;
3193                        ctx->obj2 = srv_drop(sv)) {
3194
3195                         sv = ctx->obj2;
3196                         srv_take(sv);
3197
3198                         if (htx) {
3199                                 if (htx_almost_full(htx))
3200                                         goto full;
3201                         }
3202                         else {
3203                                 if (buffer_almost_full(&rep->buf))
3204                                         goto full;
3205                         }
3206
3207                         if (ctx->flags & STAT_BOUND) {
3208                                 if (!(ctx->type & (1 << STATS_TYPE_SV))) {
3209                                         srv_drop(sv);
3210                                         break;
3211                                 }
3212
3213                                 if (ctx->sid != -1 && sv->puid != ctx->sid)
3214                                         continue;
3215                         }
3216
3217                         /* do not report disabled servers */
3218                         if (ctx->flags & STAT_HIDE_MAINT &&
3219                             sv->cur_admin & SRV_ADMF_MAINT) {
3220                                 continue;
3221                         }
3222
3223                         svs = sv;
3224                         while (svs->track)
3225                                 svs = svs->track;
3226
3227                         /* do not report servers which are DOWN and not changing state */
3228                         if ((ctx->flags & STAT_HIDE_DOWN) &&
3229                             ((sv->cur_admin & SRV_ADMF_MAINT) || /* server is in maintenance */
3230                              (sv->cur_state == SRV_ST_STOPPED && /* server is down */
3231                               (!((svs->agent.state | svs->check.state) & CHK_ST_ENABLED) ||
3232                                ((svs->agent.state & CHK_ST_ENABLED) && !svs->agent.health) ||
3233                                ((svs->check.state & CHK_ST_ENABLED) && !svs->check.health))))) {
3234                                 continue;
3235                         }
3236
3237                         if (stats_dump_sv_stats(sc, px, sv)) {
3238                                 if (!stats_putchk(rep, htx, &trash_chunk))
3239                                         goto full;
3240                         }
3241                 } /* for sv */
3242
3243                 ctx->px_st = STAT_PX_ST_BE;
3244                 __fallthrough;
3245
3246         case STAT_PX_ST_BE:
3247                 /* print the backend */
3248                 if (stats_dump_be_stats(sc, px)) {
3249                         if (!stats_putchk(rep, htx, &trash_chunk))
3250                                 goto full;
3251                         if (ctx->field)
3252                                 goto more;
3253                 }
3254
3255                 current_field = 0;
3256                 ctx->px_st = STAT_PX_ST_END;
3257                 __fallthrough;
3258
3259         case STAT_PX_ST_END:
3260                 if (ctx->flags & STAT_FMT_HTML) {
3261                         stats_dump_html_px_end(sc, px);
3262                         if (!stats_putchk(rep, htx, &trash_chunk))
3263                                 goto full;
3264                 }
3265
3266                 ctx->px_st = STAT_PX_ST_FIN;
3267                 __fallthrough;
3268
3269         case STAT_PX_ST_FIN:
3270                 return 1;
3271
3272         default:
3273                 /* unknown state, we should put an abort() here ! */
3274                 return 1;
3275         }
3276
3277   full:
3278         sc_need_room(sc);
3279         /* restore previous field */
3280         ctx->field = current_field;
3281         return 0;
3282 }
3283
3284 /* Dumps the HTTP stats head block to the local trash buffer for and uses the
3285  * per-uri parameters <uri>. The caller is responsible for clearing the local
3286  * trash buffer if needed.
3287  */
3288 static void stats_dump_html_head(struct appctx *appctx, struct uri_auth *uri)
3289 {
3290         struct show_stat_ctx *ctx = appctx->svcctx;
3291
3292         /* WARNING! This must fit in the first buffer !!! */
3293         chunk_appendf(&trash_chunk,
3294                       "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
3295                       "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
3296                       "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
3297                       "<link rel=\"icon\" href=\"data:,\">\n"
3298                       "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
3299                       "<style type=\"text/css\"><!--\n"
3300                       "body {"
3301                       " font-family: arial, helvetica, sans-serif;"
3302                       " font-size: 12px;"
3303                       " font-weight: normal;"
3304                       " color: black;"
3305                       " background: white;"
3306                       "}\n"
3307                       "th,td {"
3308                       " font-size: 10px;"
3309                       "}\n"
3310                       "h1 {"
3311                       " font-size: x-large;"
3312                       " margin-bottom: 0.5em;"
3313                       "}\n"
3314                       "h2 {"
3315                       " font-family: helvetica, arial;"
3316                       " font-size: x-large;"
3317                       " font-weight: bold;"
3318                       " font-style: italic;"
3319                       " color: #6020a0;"
3320                       " margin-top: 0em;"
3321                       " margin-bottom: 0em;"
3322                       "}\n"
3323                       "h3 {"
3324                       " font-family: helvetica, arial;"
3325                       " font-size: 16px;"
3326                       " font-weight: bold;"
3327                       " color: #b00040;"
3328                       " background: #e8e8d0;"
3329                       " margin-top: 0em;"
3330                       " margin-bottom: 0em;"
3331                       "}\n"
3332                       "li {"
3333                       " margin-top: 0.25em;"
3334                       " margin-right: 2em;"
3335                       "}\n"
3336                       ".hr {margin-top: 0.25em;"
3337                       " border-color: black;"
3338                       " border-bottom-style: solid;"
3339                       "}\n"
3340                       ".titre   {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
3341                       ".total   {background: #20D0D0;color: #ffff80;}\n"
3342                       ".frontend        {background: #e8e8d0;}\n"
3343                       ".socket  {background: #d0d0d0;}\n"
3344                       ".backend {background: #e8e8d0;}\n"
3345                       ".active_down             {background: #ff9090;}\n"
3346                       ".active_going_up         {background: #ffd020;}\n"
3347                       ".active_going_down       {background: #ffffa0;}\n"
3348                       ".active_up               {background: #c0ffc0;}\n"
3349                       ".active_nolb             {background: #20a0ff;}\n"
3350                       ".active_draining         {background: #20a0FF;}\n"
3351                       ".active_no_check         {background: #e0e0e0;}\n"
3352                       ".backup_down             {background: #ff9090;}\n"
3353                       ".backup_going_up         {background: #ff80ff;}\n"
3354                       ".backup_going_down       {background: #c060ff;}\n"
3355                       ".backup_up               {background: #b0d0ff;}\n"
3356                       ".backup_nolb             {background: #90b0e0;}\n"
3357                       ".backup_draining         {background: #cc9900;}\n"
3358                       ".backup_no_check         {background: #e0e0e0;}\n"
3359                       ".maintain        {background: #c07820;}\n"
3360                       ".rls      {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
3361                       "\n"
3362                       "a.px:link {color: #ffff40; text-decoration: none;}"
3363                       "a.px:visited {color: #ffff40; text-decoration: none;}"
3364                       "a.px:hover {color: #ffffff; text-decoration: none;}"
3365                       "a.lfsb:link {color: #000000; text-decoration: none;}"
3366                       "a.lfsb:visited {color: #000000; text-decoration: none;}"
3367                       "a.lfsb:hover {color: #505050; text-decoration: none;}"
3368                       "\n"
3369                       "table.tbl { border-collapse: collapse; border-style: none;}\n"
3370                       "table.tbl td { text-align: right; border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray; white-space: nowrap;}\n"
3371                       "table.tbl td.ac { text-align: center;}\n"
3372                       "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
3373                       "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
3374                       "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
3375                       "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
3376                       "\n"
3377                       "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
3378                       "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
3379                       "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
3380                       "table.det { border-collapse: collapse; border-style: none; }\n"
3381                       "table.det th { text-align: left; border-width: 0px; padding: 0px 1px 0px 0px; font-style:normal;font-size:11px;font-weight:bold;font-family: sans-serif;}\n"
3382                       "table.det td { text-align: right; border-width: 0px; padding: 0px 0px 0px 4px; white-space: nowrap; font-style:normal;font-size:11px;font-weight:normal;}\n"
3383                       "u {text-decoration:none; border-bottom: 1px dotted black;}\n"
3384                       "div.tips {\n"
3385                       " display:block;\n"
3386                       " visibility:hidden;\n"
3387                       " z-index:2147483647;\n"
3388                       " position:absolute;\n"
3389                       " padding:2px 4px 3px;\n"
3390                       " background:#f0f060; color:#000000;\n"
3391                       " border:1px solid #7040c0;\n"
3392                       " white-space:nowrap;\n"
3393                       " font-style:normal;font-size:11px;font-weight:normal;\n"
3394                       " -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;\n"
3395                       " -moz-box-shadow:gray 2px 2px 3px;-webkit-box-shadow:gray 2px 2px 3px;box-shadow:gray 2px 2px 3px;\n"
3396                       "}\n"
3397                       "u:hover div.tips {visibility:visible;}\n"
3398                       "@media (prefers-color-scheme: dark) {\n"
3399                       " body { font-family: arial, helvetica, sans-serif; font-size: 12px; font-weight: normal; color: #e8e6e3; background: #131516;}\n"
3400                       " h1 { color: #a265e0!important; }\n"
3401                       " h2 { color: #a265e0; }\n"
3402                       " h3 { color: #ff5190; background-color: #3e3e1f; }\n"
3403                       " a { color: #3391ff; }\n"
3404                       " input { background-color: #2f3437; }\n"
3405                       " .hr { border-color: #8c8273; }\n"
3406                       " .titre { background-color: #1aa6a6; color: #e8e6e3; }\n"
3407                       " .frontend {background: #2f3437;}\n"
3408                       " .socket {background: #2a2d2f;}\n"
3409                       " .backend {background: #2f3437;}\n"
3410                       " .active_down {background: #760000;}\n"
3411                       " .active_going_up {background: #b99200;}\n"
3412                       " .active_going_down {background: #6c6c00;}\n"
3413                       " .active_up {background: #165900;}\n"
3414                       " .active_nolb {background: #006ab9;}\n"
3415                       " .active_draining {background: #006ab9;}\n"
3416                       " .active_no_check {background: #2a2d2f;}\n"
3417                       " .backup_down {background: #760000;}\n"
3418                       " .backup_going_up {background: #7f007f;}\n"
3419                       " .backup_going_down {background: #580092;}\n"
3420                       " .backup_up {background: #2e3234;}\n"
3421                       " .backup_nolb {background: #1e3c6a;}\n"
3422                       " .backup_draining {background: #a37a00;}\n"
3423                       " .backup_no_check {background: #2a2d2f;}\n"
3424                       " .maintain {background: #9a601a;}\n"
3425                       " a.px:link {color: #d8d83b; text-decoration: none;}\n"
3426                       " a.px:visited {color: #d8d83b; text-decoration: none;}\n"
3427                       " a.px:hover {color: #ffffff; text-decoration: none;}\n"
3428                       " a.lfsb:link {color: #e8e6e3; text-decoration: none;}\n"
3429                       " a.lfsb:visited {color: #e8e6e3; text-decoration: none;}\n"
3430                       " a.lfsb:hover {color: #b5afa6; text-decoration: none;}\n"
3431                       " table.tbl th.empty { background-color: #181a1b; }\n"
3432                       " table.tbl th.desc { background: #181a1b; }\n"
3433                       " table.tbl th.pxname { background-color: #8d0033; color: #ffff46; }\n"
3434                       " table.tbl th { border-color: #808080; }\n"
3435                       " table.tbl td { border-color: #808080; }\n"
3436                       " u {text-decoration:none; border-bottom: 1px dotted #e8e6e3;}\n"
3437                       " div.tips {\n"
3438                       "  background:#8e8e0d;\n"
3439                       "  color:#e8e6e3;\n"
3440                       "  border-color: #4e2c86;\n"
3441                       "  -moz-box-shadow: #60686c 2px 2px 3px;\n"
3442                       "  -webkit-box-shadow: #60686c 2px 2px 3px;\n"
3443                       "  box-shadow: #60686c 2px 2px 3px;\n"
3444                       " }\n"
3445                       "}\n"
3446                       "-->\n"
3447                       "</style></head>\n",
3448                       (ctx->flags & STAT_SHNODE) ? " on " : "",
3449                       (ctx->flags & STAT_SHNODE) ? (uri && uri->node ? uri->node : global.node) : ""
3450                       );
3451 }
3452
3453 /* Dumps the HTML stats information block to the local trash buffer for and uses
3454  * the state from stream connector <sc> and per-uri parameters <uri>. The caller
3455  * is responsible for clearing the local trash buffer if needed.
3456  */
3457 static void stats_dump_html_info(struct stconn *sc, struct uri_auth *uri)
3458 {
3459         struct appctx *appctx = __sc_appctx(sc);
3460         struct show_stat_ctx *ctx = appctx->svcctx;
3461         unsigned int up = (now.tv_sec - start_date.tv_sec);
3462         char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
3463         const char *scope_ptr = stats_scope_ptr(appctx, sc);
3464         unsigned long long bps = (unsigned long long)read_freq_ctr(&global.out_32bps) * 32;
3465
3466         /* Turn the bytes per second to bits per second and take care of the
3467          * usual ethernet overhead in order to help figure how far we are from
3468          * interface saturation since it's the only case which usually matters.
3469          * For this we count the total size of an Ethernet frame on the wire
3470          * including preamble and IFG (1538) for the largest TCP segment it
3471          * transports (1448 with TCP timestamps). This is not valid for smaller
3472          * packets (under-estimated), but it gives a reasonably accurate
3473          * estimation of how far we are from uplink saturation.
3474          */
3475         bps = bps * 8 * 1538 / 1448;
3476
3477         /* WARNING! this has to fit the first packet too.
3478          * We are around 3.5 kB, add adding entries will
3479          * become tricky if we want to support 4kB buffers !
3480          */
3481         chunk_appendf(&trash_chunk,
3482                       "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
3483                       PRODUCT_NAME "%s</a></h1>\n"
3484                       "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
3485                       "<hr width=\"100%%\" class=\"hr\">\n"
3486                       "<h3>&gt; General process information</h3>\n"
3487                       "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
3488                       "<p><b>pid = </b> %d (process #%d, nbproc = %d, nbthread = %d)<br>\n"
3489                       "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
3490                       "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
3491                       "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>maxpipes = </b> %d<br>\n"
3492                       "current conns = %d; current pipes = %d/%d; conn rate = %d/sec; bit rate = %.3f %cbps<br>\n"
3493                       "Running tasks: %d/%d; idle = %d %%<br>\n"
3494                       "</td><td align=\"center\" nowrap>\n"
3495                       "<table class=\"lgd\"><tr>\n"
3496                       "<td class=\"active_up\">&nbsp;</td><td class=\"noborder\">active UP </td>"
3497                       "<td class=\"backup_up\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
3498                       "</tr><tr>\n"
3499                       "<td class=\"active_going_down\"></td><td class=\"noborder\">active UP, going down </td>"
3500                       "<td class=\"backup_going_down\"></td><td class=\"noborder\">backup UP, going down </td>"
3501                       "</tr><tr>\n"
3502                       "<td class=\"active_going_up\"></td><td class=\"noborder\">active DOWN, going up </td>"
3503                       "<td class=\"backup_going_up\"></td><td class=\"noborder\">backup DOWN, going up </td>"
3504                       "</tr><tr>\n"
3505                       "<td class=\"active_down\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
3506                       "<td class=\"active_no_check\"></td><td class=\"noborder\">not checked </td>"
3507                       "</tr><tr>\n"
3508                       "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) &nbsp;</td>"
3509                       "</tr><tr>\n"
3510                       "<td class=\"active_draining\"></td><td class=\"noborder\" colspan=\"3\">active or backup SOFT STOPPED for maintenance &nbsp;</td>"
3511                       "</tr></table>\n"
3512                       "Note: \"NOLB\"/\"DRAIN\" = UP with load-balancing disabled."
3513                       "</td>"
3514                       "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
3515                       "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
3516                       "",
3517                       (ctx->flags & STAT_HIDEVER) ? "" : (stats_version_string),
3518                       pid, (ctx->flags & STAT_SHNODE) ? " on " : "",
3519                       (ctx->flags & STAT_SHNODE) ? (uri->node ? uri->node : global.node) : "",
3520                       (ctx->flags & STAT_SHDESC) ? ": " : "",
3521                       (ctx->flags & STAT_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
3522                       pid, 1, 1, global.nbthread,
3523                       up / 86400, (up % 86400) / 3600,
3524                       (up % 3600) / 60, (up % 60),
3525                       global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
3526                       global.rlimit_memmax ? " MB" : "",
3527                       global.rlimit_nofile,
3528                       global.maxsock, global.maxconn, global.maxpipes,
3529                       actconn, pipes_used, pipes_used+pipes_free, read_freq_ctr(&global.conn_per_sec),
3530                       bps >= 1000000000UL ? (bps / 1000000000.0) : bps >= 1000000UL ? (bps / 1000000.0) : (bps / 1000.0),
3531                       bps >= 1000000000UL ? 'G' : bps >= 1000000UL ? 'M' : 'k',
3532                       total_run_queues(), total_allocated_tasks(), clock_report_idle()
3533                       );
3534
3535         /* scope_txt = search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
3536         memcpy(scope_txt, scope_ptr, ctx->scope_len);
3537         scope_txt[ctx->scope_len] = '\0';
3538
3539         chunk_appendf(&trash_chunk,
3540                       "<li><form method=\"GET\">Scope : <input value=\"%s\" name=\"" STAT_SCOPE_INPUT_NAME "\" size=\"8\" maxlength=\"%d\" tabindex=\"1\"/></form>\n",
3541                       (ctx->scope_len > 0) ? scope_txt : "",
3542                       STAT_SCOPE_TXT_MAXLEN);
3543
3544         /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
3545         scope_txt[0] = 0;
3546         if (ctx->scope_len) {
3547                 strcpy(scope_txt, STAT_SCOPE_PATTERN);
3548                 memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
3549                 scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
3550         }
3551
3552         if (ctx->flags & STAT_HIDE_DOWN)
3553                 chunk_appendf(&trash_chunk,
3554                               "<li><a href=\"%s%s%s%s\">Show all servers</a><br>\n",
3555                               uri->uri_prefix,
3556                               "",
3557                               (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3558                               scope_txt);
3559         else
3560                 chunk_appendf(&trash_chunk,
3561                               "<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
3562                               uri->uri_prefix,
3563                               ";up",
3564                               (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3565                               scope_txt);
3566
3567         if (uri->refresh > 0) {
3568                 if (ctx->flags & STAT_NO_REFRESH)
3569                         chunk_appendf(&trash_chunk,
3570                                       "<li><a href=\"%s%s%s%s\">Enable refresh</a><br>\n",
3571                                       uri->uri_prefix,
3572                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3573                                       "",
3574                                       scope_txt);
3575                 else
3576                         chunk_appendf(&trash_chunk,
3577                                       "<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
3578                                       uri->uri_prefix,
3579                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3580                                       ";norefresh",
3581                                       scope_txt);
3582         }
3583
3584         chunk_appendf(&trash_chunk,
3585                       "<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
3586                       uri->uri_prefix,
3587                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3588                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3589                       scope_txt);
3590
3591         chunk_appendf(&trash_chunk,
3592                       "<li><a href=\"%s;csv%s%s\">CSV export</a><br>\n",
3593                       uri->uri_prefix,
3594                       (uri->refresh > 0) ? ";norefresh" : "",
3595                       scope_txt);
3596
3597         chunk_appendf(&trash_chunk,
3598                       "<li><a href=\"%s;json%s%s\">JSON export</a> (<a href=\"%s;json-schema\">schema</a>)<br>\n",
3599                       uri->uri_prefix,
3600                       (uri->refresh > 0) ? ";norefresh" : "",
3601                       scope_txt, uri->uri_prefix);
3602
3603         chunk_appendf(&trash_chunk,
3604                       "</ul></td>"
3605                       "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
3606                       "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
3607                       "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
3608                       "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
3609                       "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
3610                       "</ul>"
3611                       "</td>"
3612                       "</tr></table>\n"
3613                       ""
3614                       );
3615
3616         if (ctx->st_code) {
3617                 switch (ctx->st_code) {
3618                 case STAT_STATUS_DONE:
3619                         chunk_appendf(&trash_chunk,
3620                                       "<p><div class=active_up>"
3621                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3622                                       "Action processed successfully."
3623                                       "</div>\n", uri->uri_prefix,
3624                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3625                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3626                                       scope_txt);
3627                         break;
3628                 case STAT_STATUS_NONE:
3629                         chunk_appendf(&trash_chunk,
3630                                       "<p><div class=active_going_down>"
3631                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3632                                       "Nothing has changed."
3633                                       "</div>\n", uri->uri_prefix,
3634                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3635                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3636                                       scope_txt);
3637                         break;
3638                 case STAT_STATUS_PART:
3639                         chunk_appendf(&trash_chunk,
3640                                       "<p><div class=active_going_down>"
3641                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3642                                       "Action partially processed.<br>"
3643                                       "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
3644                                       "</div>\n", uri->uri_prefix,
3645                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3646                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3647                                       scope_txt);
3648                         break;
3649                 case STAT_STATUS_ERRP:
3650                         chunk_appendf(&trash_chunk,
3651                                       "<p><div class=active_down>"
3652                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3653                                       "Action not processed because of invalid parameters."
3654                                       "<ul>"
3655                                       "<li>The action is maybe unknown.</li>"
3656                                       "<li>Invalid key parameter (empty or too long).</li>"
3657                                       "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
3658                                       "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
3659                                       "</ul>"
3660                                       "</div>\n", uri->uri_prefix,
3661                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3662                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3663                                       scope_txt);
3664                         break;
3665                 case STAT_STATUS_EXCD:
3666                         chunk_appendf(&trash_chunk,
3667                                       "<p><div class=active_down>"
3668                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3669                                       "<b>Action not processed : the buffer couldn't store all the data.<br>"
3670                                       "You should retry with less servers at a time.</b>"
3671                                       "</div>\n", uri->uri_prefix,
3672                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3673                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3674                                       scope_txt);
3675                         break;
3676                 case STAT_STATUS_DENY:
3677                         chunk_appendf(&trash_chunk,
3678                                       "<p><div class=active_down>"
3679                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3680                                       "<b>Action denied.</b>"
3681                                       "</div>\n", uri->uri_prefix,
3682                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3683                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3684                                       scope_txt);
3685                         break;
3686                 case STAT_STATUS_IVAL:
3687                         chunk_appendf(&trash_chunk,
3688                                       "<p><div class=active_down>"
3689                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3690                                       "<b>Invalid requests (unsupported method or chunked encoded request).</b>"
3691                                       "</div>\n", uri->uri_prefix,
3692                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3693                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3694                                       scope_txt);
3695                         break;
3696                 default:
3697                         chunk_appendf(&trash_chunk,
3698                                       "<p><div class=active_no_check>"
3699                                       "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
3700                                       "Unexpected result."
3701                                       "</div>\n", uri->uri_prefix,
3702                                       (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
3703                                       (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
3704                                       scope_txt);
3705                 }
3706                 chunk_appendf(&trash_chunk, "<p>\n");
3707         }
3708 }
3709
3710 /* Dumps the HTML stats trailer block to the local trash buffer. The caller is
3711  * responsible for clearing the local trash buffer if needed.
3712  */
3713 static void stats_dump_html_end()
3714 {
3715         chunk_appendf(&trash_chunk, "</body></html>\n");
3716 }
3717
3718 /* Dumps the stats JSON header to the local trash buffer buffer which. The
3719  * caller is responsible for clearing it if needed.
3720  */
3721 static void stats_dump_json_header()
3722 {
3723         chunk_strcat(&trash_chunk, "[");
3724 }
3725
3726
3727 /* Dumps the JSON stats trailer block to the local trash buffer. The caller is
3728  * responsible for clearing the local trash buffer if needed.
3729  */
3730 static void stats_dump_json_end()
3731 {
3732         chunk_strcat(&trash_chunk, "]\n");
3733 }
3734
3735 /* Uses <appctx.ctx.stats.obj1> as a pointer to the current proxy and <obj2> as
3736  * a pointer to the current server/listener.
3737  */
3738 static int stats_dump_proxies(struct stconn *sc,
3739                               struct htx *htx,
3740                               struct uri_auth *uri)
3741 {
3742         struct appctx *appctx = __sc_appctx(sc);
3743         struct show_stat_ctx *ctx = appctx->svcctx;
3744         struct channel *rep = sc_ic(sc);
3745         struct proxy *px;
3746
3747         /* dump proxies */
3748         while (ctx->obj1) {
3749                 if (htx) {
3750                         if (htx_almost_full(htx))
3751                                 goto full;
3752                 }
3753                 else {
3754                         if (buffer_almost_full(&rep->buf))
3755                                 goto full;
3756                 }
3757
3758                 px = ctx->obj1;
3759                 /* Skip the global frontend proxies and non-networked ones.
3760                  * Also skip proxies that were disabled in the configuration
3761                  * This change allows retrieving stats from "old" proxies after a reload.
3762                  */
3763                 if (!(px->flags & PR_FL_DISABLED) && px->uuid > 0 &&
3764                     (px->cap & (PR_CAP_FE | PR_CAP_BE)) && !(px->cap & PR_CAP_INT)) {
3765                         if (stats_dump_proxy_to_buffer(sc, htx, px, uri) == 0)
3766                                 return 0;
3767                 }
3768
3769                 ctx->obj1 = px->next;
3770                 ctx->px_st = STAT_PX_ST_INIT;
3771                 ctx->field = 0;
3772         }
3773
3774         return 1;
3775
3776   full:
3777         sc_need_room(sc);
3778         return 0;
3779 }
3780
3781 /* This function dumps statistics onto the stream connector's read buffer in
3782  * either CSV or HTML format. <uri> contains some HTML-specific parameters that
3783  * are ignored for CSV format (hence <uri> may be NULL there). It returns 0 if
3784  * it had to stop writing data and an I/O is needed, 1 if the dump is finished
3785  * and the stream must be closed, or -1 in case of any error. This function is
3786  * used by both the CLI and the HTTP handlers.
3787  */
3788 static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx,
3789                                      struct uri_auth *uri)
3790 {
3791         struct appctx *appctx = __sc_appctx(sc);
3792         struct show_stat_ctx *ctx = appctx->svcctx;
3793         struct channel *rep = sc_ic(sc);
3794         enum stats_domain domain = ctx->domain;
3795
3796         chunk_reset(&trash_chunk);
3797
3798         switch (ctx->state) {
3799         case STAT_STATE_INIT:
3800                 ctx->state = STAT_STATE_HEAD; /* let's start producing data */
3801                 __fallthrough;
3802
3803         case STAT_STATE_HEAD:
3804                 if (ctx->flags & STAT_FMT_HTML)
3805                         stats_dump_html_head(appctx, uri);
3806                 else if (ctx->flags & STAT_JSON_SCHM)
3807                         stats_dump_json_schema(&trash_chunk);
3808                 else if (ctx->flags & STAT_FMT_JSON)
3809                         stats_dump_json_header();
3810                 else if (!(ctx->flags & STAT_FMT_TYPED))
3811                         stats_dump_csv_header(ctx->domain);
3812
3813                 if (!stats_putchk(rep, htx, &trash_chunk))
3814                         goto full;
3815
3816                 if (ctx->flags & STAT_JSON_SCHM) {
3817                         ctx->state = STAT_STATE_FIN;
3818                         return 1;
3819                 }
3820                 ctx->state = STAT_STATE_INFO;
3821                 __fallthrough;
3822
3823         case STAT_STATE_INFO:
3824                 if (ctx->flags & STAT_FMT_HTML) {
3825                         stats_dump_html_info(sc, uri);
3826                         if (!stats_putchk(rep, htx, &trash_chunk))
3827                                 goto full;
3828                 }
3829
3830                 if (domain == STATS_DOMAIN_PROXY)
3831                         ctx->obj1 = proxies_list;
3832
3833                 ctx->px_st = STAT_PX_ST_INIT;
3834                 ctx->field = 0;
3835                 ctx->state = STAT_STATE_LIST;
3836                 __fallthrough;
3837
3838         case STAT_STATE_LIST:
3839                 switch (domain) {
3840                 case STATS_DOMAIN_RESOLVERS:
3841                         if (!stats_dump_resolvers(sc, stat_l[domain],
3842                                                   stat_count[domain],
3843                                                   &stats_module_list[domain])) {
3844                                 return 0;
3845                         }
3846                         break;
3847
3848                 case STATS_DOMAIN_PROXY:
3849                 default:
3850                         /* dump proxies */
3851                         if (!stats_dump_proxies(sc, htx, uri))
3852                                 return 0;
3853                         break;
3854                 }
3855
3856                 ctx->state = STAT_STATE_END;
3857                 __fallthrough;
3858
3859         case STAT_STATE_END:
3860                 if (ctx->flags & (STAT_FMT_HTML|STAT_FMT_JSON)) {
3861                         if (ctx->flags & STAT_FMT_HTML)
3862                                 stats_dump_html_end();
3863                         else
3864                                 stats_dump_json_end();
3865                         if (!stats_putchk(rep, htx, &trash_chunk))
3866                                 goto full;
3867                 }
3868
3869                 ctx->state = STAT_STATE_FIN;
3870                 __fallthrough;
3871
3872         case STAT_STATE_FIN:
3873                 return 1;
3874
3875         default:
3876                 /* unknown state ! */
3877                 ctx->state = STAT_STATE_FIN;
3878                 return -1;
3879         }
3880
3881   full:
3882         sc_need_room(sc);
3883         return 0;
3884
3885 }
3886
3887 /* We reached the stats page through a POST request. The appctx is
3888  * expected to have already been allocated by the caller.
3889  * Parse the posted data and enable/disable servers if necessary.
3890  * Returns 1 if request was parsed or zero if it needs more data.
3891  */
3892 static int stats_process_http_post(struct stconn *sc)
3893 {
3894         struct stream *s = __sc_strm(sc);
3895         struct appctx *appctx = __sc_appctx(sc);
3896         struct show_stat_ctx *ctx = appctx->svcctx;
3897
3898         struct proxy *px = NULL;
3899         struct server *sv = NULL;
3900
3901         char key[LINESIZE];
3902         int action = ST_ADM_ACTION_NONE;
3903         int reprocess = 0;
3904
3905         int total_servers = 0;
3906         int altered_servers = 0;
3907
3908         char *first_param, *cur_param, *next_param, *end_params;
3909         char *st_cur_param = NULL;
3910         char *st_next_param = NULL;
3911
3912         struct buffer *temp = get_trash_chunk();
3913
3914         struct htx *htx = htxbuf(&s->req.buf);
3915         struct htx_blk *blk;
3916
3917         /*  we need more data */
3918         if (s->txn->req.msg_state < HTTP_MSG_DONE) {
3919                 /* check if we can receive more */
3920                 if (htx_free_data_space(htx) <= global.tune.maxrewrite) {
3921                         ctx->st_code = STAT_STATUS_EXCD;
3922                         goto out;
3923                 }
3924                 goto wait;
3925         }
3926
3927         /* The request was fully received. Copy data */
3928         blk = htx_get_head_blk(htx);
3929         while (blk) {
3930                 enum htx_blk_type type = htx_get_blk_type(blk);
3931
3932                 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
3933                         break;
3934                 if (type == HTX_BLK_DATA) {
3935                         struct ist v = htx_get_blk_value(htx, blk);
3936
3937                         if (!chunk_memcat(temp, v.ptr, v.len)) {
3938                                 ctx->st_code = STAT_STATUS_EXCD;
3939                                 goto out;
3940                         }
3941                 }
3942                 blk = htx_get_next_blk(htx, blk);
3943         }
3944
3945         first_param = temp->area;
3946         end_params  = temp->area + temp->data;
3947         cur_param = next_param = end_params;
3948         *end_params = '\0';
3949
3950         ctx->st_code = STAT_STATUS_NONE;
3951
3952         /*
3953          * Parse the parameters in reverse order to only store the last value.
3954          * From the html form, the backend and the action are at the end.
3955          */
3956         while (cur_param > first_param) {
3957                 char *value;
3958                 int poffset, plen;
3959
3960                 cur_param--;
3961
3962                 if ((*cur_param == '&') || (cur_param == first_param)) {
3963                 reprocess_servers:
3964                         /* Parse the key */
3965                         poffset = (cur_param != first_param ? 1 : 0);
3966                         plen = next_param - cur_param + (cur_param == first_param ? 1 : 0);
3967                         if ((plen > 0) && (plen <= sizeof(key))) {
3968                                 strncpy(key, cur_param + poffset, plen);
3969                                 key[plen - 1] = '\0';
3970                         } else {
3971                                 ctx->st_code = STAT_STATUS_ERRP;
3972                                 goto out;
3973                         }
3974
3975                         /* Parse the value */
3976                         value = key;
3977                         while (*value != '\0' && *value != '=') {
3978                                 value++;
3979                         }
3980                         if (*value == '=') {
3981                                 /* Ok, a value is found, we can mark the end of the key */
3982                                 *value++ = '\0';
3983                         }
3984                         if (url_decode(key, 1) < 0 || url_decode(value, 1) < 0)
3985                                 break;
3986
3987                         /* Now we can check the key to see what to do */
3988                         if (!px && (strcmp(key, "b") == 0)) {
3989                                 if ((px = proxy_be_by_name(value)) == NULL) {
3990                                         /* the backend name is unknown or ambiguous (duplicate names) */
3991                                         ctx->st_code = STAT_STATUS_ERRP;
3992                                         goto out;
3993                                 }
3994                         }
3995                         else if (!action && (strcmp(key, "action") == 0)) {
3996                                 if (strcmp(value, "ready") == 0) {
3997                                         action = ST_ADM_ACTION_READY;
3998                                 }
3999                                 else if (strcmp(value, "drain") == 0) {
4000                                         action = ST_ADM_ACTION_DRAIN;
4001                                 }
4002                                 else if (strcmp(value, "maint") == 0) {
4003                                         action = ST_ADM_ACTION_MAINT;
4004                                 }
4005                                 else if (strcmp(value, "shutdown") == 0) {
4006                                         action = ST_ADM_ACTION_SHUTDOWN;
4007                                 }
4008                                 else if (strcmp(value, "dhlth") == 0) {
4009                                         action = ST_ADM_ACTION_DHLTH;
4010                                 }
4011                                 else if (strcmp(value, "ehlth") == 0) {
4012                                         action = ST_ADM_ACTION_EHLTH;
4013                                 }
4014                                 else if (strcmp(value, "hrunn") == 0) {
4015                                         action = ST_ADM_ACTION_HRUNN;
4016                                 }
4017                                 else if (strcmp(value, "hnolb") == 0) {
4018                                         action = ST_ADM_ACTION_HNOLB;
4019                                 }
4020                                 else if (strcmp(value, "hdown") == 0) {
4021                                         action = ST_ADM_ACTION_HDOWN;
4022                                 }
4023                                 else if (strcmp(value, "dagent") == 0) {
4024                                         action = ST_ADM_ACTION_DAGENT;
4025                                 }
4026                                 else if (strcmp(value, "eagent") == 0) {
4027                                         action = ST_ADM_ACTION_EAGENT;
4028                                 }
4029                                 else if (strcmp(value, "arunn") == 0) {
4030                                         action = ST_ADM_ACTION_ARUNN;
4031                                 }
4032                                 else if (strcmp(value, "adown") == 0) {
4033                                         action = ST_ADM_ACTION_ADOWN;
4034                                 }
4035                                 /* else these are the old supported methods */
4036                                 else if (strcmp(value, "disable") == 0) {
4037                                         action = ST_ADM_ACTION_DISABLE;
4038                                 }
4039                                 else if (strcmp(value, "enable") == 0) {
4040                                         action = ST_ADM_ACTION_ENABLE;
4041                                 }
4042                                 else if (strcmp(value, "stop") == 0) {
4043                                         action = ST_ADM_ACTION_STOP;
4044                                 }
4045                                 else if (strcmp(value, "start") == 0) {
4046                                         action = ST_ADM_ACTION_START;
4047                                 }
4048                                 else {
4049                                         ctx->st_code = STAT_STATUS_ERRP;
4050                                         goto out;
4051                                 }
4052                         }
4053                         else if (strcmp(key, "s") == 0) {
4054                                 if (!(px && action)) {
4055                                         /*
4056                                          * Indicates that we'll need to reprocess the parameters
4057                                          * as soon as backend and action are known
4058                                          */
4059                                         if (!reprocess) {
4060                                                 st_cur_param  = cur_param;
4061                                                 st_next_param = next_param;
4062                                         }
4063                                         reprocess = 1;
4064                                 }
4065                                 else if ((sv = findserver(px, value)) != NULL) {
4066                                         HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
4067                                         switch (action) {
4068                                         case ST_ADM_ACTION_DISABLE:
4069                                                 if (!(sv->cur_admin & SRV_ADMF_FMAINT)) {
4070                                                         altered_servers++;
4071                                                         total_servers++;
4072                                                         srv_set_admin_flag(sv, SRV_ADMF_FMAINT, "'disable' on stats page");
4073                                                 }
4074                                                 break;
4075                                         case ST_ADM_ACTION_ENABLE:
4076                                                 if (sv->cur_admin & SRV_ADMF_FMAINT) {
4077                                                         altered_servers++;
4078                                                         total_servers++;
4079                                                         srv_clr_admin_flag(sv, SRV_ADMF_FMAINT);
4080                                                 }
4081                                                 break;
4082                                         case ST_ADM_ACTION_STOP:
4083                                                 if (!(sv->cur_admin & SRV_ADMF_FDRAIN)) {
4084                                                         srv_set_admin_flag(sv, SRV_ADMF_FDRAIN, "'stop' on stats page");
4085                                                         altered_servers++;
4086                                                         total_servers++;
4087                                                 }
4088                                                 break;
4089                                         case ST_ADM_ACTION_START:
4090                                                 if (sv->cur_admin & SRV_ADMF_FDRAIN) {
4091                                                         srv_clr_admin_flag(sv, SRV_ADMF_FDRAIN);
4092                                                         altered_servers++;
4093                                                         total_servers++;
4094                                                 }
4095                                                 break;
4096                                         case ST_ADM_ACTION_DHLTH:
4097                                                 if (sv->check.state & CHK_ST_CONFIGURED) {
4098                                                         sv->check.state &= ~CHK_ST_ENABLED;
4099                                                         altered_servers++;
4100                                                         total_servers++;
4101                                                 }
4102                                                 break;
4103                                         case ST_ADM_ACTION_EHLTH:
4104                                                 if (sv->check.state & CHK_ST_CONFIGURED) {
4105                                                         sv->check.state |= CHK_ST_ENABLED;
4106                                                         altered_servers++;
4107                                                         total_servers++;
4108                                                 }
4109                                                 break;
4110                                         case ST_ADM_ACTION_HRUNN:
4111                                                 if (!(sv->track)) {
4112                                                         sv->check.health = sv->check.rise + sv->check.fall - 1;
4113                                                         srv_set_running(sv, "changed from Web interface", NULL);
4114                                                         altered_servers++;
4115                                                         total_servers++;
4116                                                 }
4117                                                 break;
4118                                         case ST_ADM_ACTION_HNOLB:
4119                                                 if (!(sv->track)) {
4120                                                         sv->check.health = sv->check.rise + sv->check.fall - 1;
4121                                                         srv_set_stopping(sv, "changed from Web interface", NULL);
4122                                                         altered_servers++;
4123                                                         total_servers++;
4124                                                 }
4125                                                 break;
4126                                         case ST_ADM_ACTION_HDOWN:
4127                                                 if (!(sv->track)) {
4128                                                         sv->check.health = 0;
4129                                                         srv_set_stopped(sv, "changed from Web interface", NULL);
4130                                                         altered_servers++;
4131                                                         total_servers++;
4132                                                 }
4133                                                 break;
4134                                         case ST_ADM_ACTION_DAGENT:
4135                                                 if (sv->agent.state & CHK_ST_CONFIGURED) {
4136                                                         sv->agent.state &= ~CHK_ST_ENABLED;
4137                                                         altered_servers++;
4138                                                         total_servers++;
4139                                                 }
4140                                                 break;
4141                                         case ST_ADM_ACTION_EAGENT:
4142                                                 if (sv->agent.state & CHK_ST_CONFIGURED) {
4143                                                         sv->agent.state |= CHK_ST_ENABLED;
4144                                                         altered_servers++;
4145                                                         total_servers++;
4146                                                 }
4147                                                 break;
4148                                         case ST_ADM_ACTION_ARUNN:
4149                                                 if (sv->agent.state & CHK_ST_ENABLED) {
4150                                                         sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
4151                                                         srv_set_running(sv, "changed from Web interface", NULL);
4152                                                         altered_servers++;
4153                                                         total_servers++;
4154                                                 }
4155                                                 break;
4156                                         case ST_ADM_ACTION_ADOWN:
4157                                                 if (sv->agent.state & CHK_ST_ENABLED) {
4158                                                         sv->agent.health = 0;
4159                                                         srv_set_stopped(sv, "changed from Web interface", NULL);
4160                                                         altered_servers++;
4161                                                         total_servers++;
4162                                                 }
4163                                                 break;
4164                                         case ST_ADM_ACTION_READY:
4165                                                 srv_adm_set_ready(sv);
4166                                                 altered_servers++;
4167                                                 total_servers++;
4168                                                 break;
4169                                         case ST_ADM_ACTION_DRAIN:
4170                                                 srv_adm_set_drain(sv);
4171                                                 altered_servers++;
4172                                                 total_servers++;
4173                                                 break;
4174                                         case ST_ADM_ACTION_MAINT:
4175                                                 srv_adm_set_maint(sv);
4176                                                 altered_servers++;
4177                                                 total_servers++;
4178                                                 break;
4179                                         case ST_ADM_ACTION_SHUTDOWN:
4180                                                 if (!(px->flags & (PR_FL_DISABLED|PR_FL_STOPPED))) {
4181                                                         srv_shutdown_streams(sv, SF_ERR_KILLED);
4182                                                         altered_servers++;
4183                                                         total_servers++;
4184                                                 }
4185                                                 break;
4186                                         }
4187                                         HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
4188                                 } else {
4189                                         /* the server name is unknown or ambiguous (duplicate names) */
4190                                         total_servers++;
4191                                 }
4192                         }
4193                         if (reprocess && px && action) {
4194                                 /* Now, we know the backend and the action chosen by the user.
4195                                  * We can safely restart from the first server parameter
4196                                  * to reprocess them
4197                                  */
4198                                 cur_param  = st_cur_param;
4199                                 next_param = st_next_param;
4200                                 reprocess = 0;
4201                                 goto reprocess_servers;
4202                         }
4203
4204                         next_param = cur_param;
4205                 }
4206         }
4207
4208         if (total_servers == 0) {
4209                 ctx->st_code = STAT_STATUS_NONE;
4210         }
4211         else if (altered_servers == 0) {
4212                 ctx->st_code = STAT_STATUS_ERRP;
4213         }
4214         else if (altered_servers == total_servers) {
4215                 ctx->st_code = STAT_STATUS_DONE;
4216         }
4217         else {
4218                 ctx->st_code = STAT_STATUS_PART;
4219         }
4220  out:
4221         return 1;
4222  wait:
4223         ctx->st_code = STAT_STATUS_NONE;
4224         return 0;
4225 }
4226
4227
4228 static int stats_send_http_headers(struct stconn *sc, struct htx *htx)
4229 {
4230         struct stream *s = __sc_strm(sc);
4231         struct uri_auth *uri = s->be->uri_auth;
4232         struct appctx *appctx = __sc_appctx(sc);
4233         struct show_stat_ctx *ctx = appctx->svcctx;
4234         struct htx_sl *sl;
4235         unsigned int flags;
4236
4237         flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_ENC|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
4238         sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("200"), ist("OK"));
4239         if (!sl)
4240                 goto full;
4241         sl->info.res.status = 200;
4242
4243         if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")))
4244                 goto full;
4245         if (ctx->flags & STAT_FMT_HTML) {
4246                 if (!htx_add_header(htx, ist("Content-Type"), ist("text/html")))
4247                         goto full;
4248         }
4249         else if (ctx->flags & (STAT_FMT_JSON|STAT_JSON_SCHM)) {
4250                 if (!htx_add_header(htx, ist("Content-Type"), ist("application/json")))
4251                         goto full;
4252         }
4253         else {
4254                 if (!htx_add_header(htx, ist("Content-Type"), ist("text/plain")))
4255                         goto full;
4256         }
4257
4258         if (uri->refresh > 0 && !(ctx->flags & STAT_NO_REFRESH)) {
4259                 const char *refresh = U2A(uri->refresh);
4260                 if (!htx_add_header(htx, ist("Refresh"), ist(refresh)))
4261                         goto full;
4262         }
4263
4264         if (ctx->flags & STAT_CHUNKED) {
4265                 if (!htx_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
4266                         goto full;
4267         }
4268
4269         if (!htx_add_endof(htx, HTX_BLK_EOH))
4270                 goto full;
4271
4272         channel_add_input(&s->res, htx->data);
4273         return 1;
4274
4275   full:
4276         htx_reset(htx);
4277         sc_need_room(sc);
4278         return 0;
4279 }
4280
4281
4282 static int stats_send_http_redirect(struct stconn *sc, struct htx *htx)
4283 {
4284         char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
4285         struct stream *s = __sc_strm(sc);
4286         struct uri_auth *uri = s->be->uri_auth;
4287         struct appctx *appctx = __sc_appctx(sc);
4288         struct show_stat_ctx *ctx = appctx->svcctx;
4289         struct htx_sl *sl;
4290         unsigned int flags;
4291
4292         /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
4293         scope_txt[0] = 0;
4294         if (ctx->scope_len) {
4295                 const char *scope_ptr = stats_scope_ptr(appctx, sc);
4296
4297                 strcpy(scope_txt, STAT_SCOPE_PATTERN);
4298                 memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
4299                 scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
4300         }
4301
4302         /* We don't want to land on the posted stats page because a refresh will
4303          * repost the data. We don't want this to happen on accident so we redirect
4304          * the browse to the stats page with a GET.
4305          */
4306         chunk_printf(&trash, "%s;st=%s%s%s%s",
4307                      uri->uri_prefix,
4308                      ((ctx->st_code > STAT_STATUS_INIT) &&
4309                       (ctx->st_code < STAT_STATUS_SIZE) &&
4310                       stat_status_codes[ctx->st_code]) ?
4311                      stat_status_codes[ctx->st_code] :
4312                      stat_status_codes[STAT_STATUS_UNKN],
4313                      (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
4314                      (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
4315                      scope_txt);
4316
4317         flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
4318         sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("303"), ist("See Other"));
4319         if (!sl)
4320                 goto full;
4321         sl->info.res.status = 303;
4322
4323         if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
4324             !htx_add_header(htx, ist("Content-Type"), ist("text/plain")) ||
4325             !htx_add_header(htx, ist("Content-Length"), ist("0")) ||
4326             !htx_add_header(htx, ist("Location"), ist2(trash.area, trash.data)))
4327                 goto full;
4328
4329         if (!htx_add_endof(htx, HTX_BLK_EOH))
4330                 goto full;
4331
4332         channel_add_input(&s->res, htx->data);
4333         return 1;
4334
4335 full:
4336         htx_reset(htx);
4337         sc_need_room(sc);
4338         return 0;
4339 }
4340
4341
4342 /* This I/O handler runs as an applet embedded in a stream connector. It is
4343  * used to send HTTP stats over a TCP socket. The mechanism is very simple.
4344  * appctx->st0 contains the operation in progress (dump, done). The handler
4345  * automatically unregisters itself once transfer is complete.
4346  */
4347 static void http_stats_io_handler(struct appctx *appctx)
4348 {
4349         struct show_stat_ctx *ctx = appctx->svcctx;
4350         struct stconn *sc = appctx_sc(appctx);
4351         struct stream *s = __sc_strm(sc);
4352         struct channel *req = sc_oc(sc);
4353         struct channel *res = sc_ic(sc);
4354         struct htx *req_htx, *res_htx;
4355
4356         /* only proxy stats are available via http */
4357         ctx->domain = STATS_DOMAIN_PROXY;
4358
4359         res_htx = htx_from_buf(&res->buf);
4360
4361         if (unlikely(sc->state == SC_ST_DIS || sc->state == SC_ST_CLO))
4362                 goto out;
4363
4364         /* Check if the input buffer is available. */
4365         if (!b_size(&res->buf)) {
4366                 sc_need_room(sc);
4367                 goto out;
4368         }
4369
4370         /* check that the output is not closed */
4371         if (res->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_SHUTR))
4372                 appctx->st0 = STAT_HTTP_END;
4373
4374         /* all states are processed in sequence */
4375         if (appctx->st0 == STAT_HTTP_HEAD) {
4376                 if (stats_send_http_headers(sc, res_htx)) {
4377                         if (s->txn->meth == HTTP_METH_HEAD)
4378                                 appctx->st0 = STAT_HTTP_DONE;
4379                         else
4380                                 appctx->st0 = STAT_HTTP_DUMP;
4381                 }
4382         }
4383
4384         if (appctx->st0 == STAT_HTTP_DUMP) {
4385                 trash_chunk = b_make(trash.area, channel_htx_recv_limit(res, res_htx), 0, 0);
4386                 if (stats_dump_stat_to_buffer(sc, res_htx, s->be->uri_auth))
4387                         appctx->st0 = STAT_HTTP_DONE;
4388         }
4389
4390         if (appctx->st0 == STAT_HTTP_POST) {
4391                 if (stats_process_http_post(sc))
4392                         appctx->st0 = STAT_HTTP_LAST;
4393                 else if (req->flags & CF_SHUTR)
4394                         appctx->st0 = STAT_HTTP_DONE;
4395         }
4396
4397         if (appctx->st0 == STAT_HTTP_LAST) {
4398                 if (stats_send_http_redirect(sc, res_htx))
4399                         appctx->st0 = STAT_HTTP_DONE;
4400         }
4401
4402         if (appctx->st0 == STAT_HTTP_DONE) {
4403                 /* no more data are expected. If the response buffer is empty,
4404                  * be sure to add something (EOT block in this case) to have
4405                  * something to send. It is important to be sure the EOM flags
4406                  * will be handled by the endpoint.
4407                  */
4408                 if (htx_is_empty(res_htx)) {
4409                         if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
4410                                 sc_need_room(sc);
4411                                 goto out;
4412                         }
4413                         channel_add_input(res, 1);
4414                 }
4415                 res_htx->flags |= HTX_FL_EOM;
4416                 res->flags |= CF_EOI;
4417                 se_fl_set(appctx->sedesc, SE_FL_EOI);
4418                 appctx->st0 = STAT_HTTP_END;
4419         }
4420
4421         if (appctx->st0 == STAT_HTTP_END) {
4422                 if (!(res->flags & CF_SHUTR)) {
4423                         res->flags |= CF_READ_NULL;
4424                         sc_shutr(sc);
4425                 }
4426
4427                 /* eat the whole request */
4428                 if (co_data(req)) {
4429                         req_htx = htx_from_buf(&req->buf);
4430                         co_htx_skip(req, req_htx, co_data(req));
4431                         htx_to_buf(req_htx, &req->buf);
4432                 }
4433         }
4434
4435  out:
4436         /* we have left the request in the buffer for the case where we
4437          * process a POST, and this automatically re-enables activity on
4438          * read. It's better to indicate that we want to stop reading when
4439          * we're sending, so that we know there's at most one direction
4440          * deciding to wake the applet up. It saves it from looping when
4441          * emitting large blocks into small TCP windows.
4442          */
4443         htx_to_buf(res_htx, &res->buf);
4444         if (!channel_is_empty(res))
4445                 applet_wont_consume(appctx);
4446 }
4447
4448 /* Dump all fields from <info> into <out> using the "show info" format (name: value) */
4449 static int stats_dump_info_fields(struct buffer *out,
4450                                   const struct field *info,
4451                                   struct show_stat_ctx *ctx)
4452 {
4453         int flags = ctx->flags;
4454         int field;
4455
4456         for (field = 0; field < INF_TOTAL_FIELDS; field++) {
4457                 if (!field_format(info, field))
4458                         continue;
4459
4460                 if (!chunk_appendf(out, "%s: ", info_fields[field].name))
4461                         return 0;
4462                 if (!stats_emit_raw_data_field(out, &info[field]))
4463                         return 0;
4464                 if ((flags & STAT_SHOW_FDESC) && !chunk_appendf(out, ":\"%s\"", info_fields[field].desc))
4465                         return 0;
4466                 if (!chunk_strcat(out, "\n"))
4467                         return 0;
4468         }
4469         return 1;
4470 }
4471
4472 /* Dump all fields from <info> into <out> using the "show info typed" format */
4473 static int stats_dump_typed_info_fields(struct buffer *out,
4474                                         const struct field *info,
4475                                         struct show_stat_ctx *ctx)
4476 {
4477         int flags = ctx->flags;
4478         int field;
4479
4480         for (field = 0; field < INF_TOTAL_FIELDS; field++) {
4481                 if (!field_format(info, field))
4482                         continue;
4483
4484                 if (!chunk_appendf(out, "%d.%s.%u:", field, info_fields[field].name, info[INF_PROCESS_NUM].u.u32))
4485                         return 0;
4486                 if (!stats_emit_field_tags(out, &info[field], ':'))
4487                         return 0;
4488                 if (!stats_emit_typed_data_field(out, &info[field]))
4489                         return 0;
4490                 if ((flags & STAT_SHOW_FDESC) && !chunk_appendf(out, ":\"%s\"", info_fields[field].desc))
4491                         return 0;
4492                 if (!chunk_strcat(out, "\n"))
4493                         return 0;
4494         }
4495         return 1;
4496 }
4497
4498 /* Fill <info> with HAProxy global info. <info> is preallocated array of length
4499  * <len>. The length of the array must be INF_TOTAL_FIELDS. If this length is
4500  * less then this value, the function returns 0, otherwise, it returns 1. Some
4501  * fields' presence or precision may depend on some of the STAT_* flags present
4502  * in <flags>.
4503  */
4504 int stats_fill_info(struct field *info, int len, uint flags)
4505 {
4506         struct timeval up;
4507         struct buffer *out = get_trash_chunk();
4508
4509 #ifdef USE_OPENSSL
4510         double ssl_sess_rate = read_freq_ctr_flt(&global.ssl_per_sec);
4511         double ssl_key_rate  = read_freq_ctr_flt(&global.ssl_fe_keys_per_sec);
4512         double ssl_reuse = 0;
4513
4514         if (ssl_key_rate < ssl_sess_rate)
4515                 ssl_reuse = 100.0 * (1.0 - ssl_key_rate / ssl_sess_rate);
4516 #endif
4517
4518         tv_remain(&start_date, &now, &up);
4519
4520         if (len < INF_TOTAL_FIELDS)
4521                 return 0;
4522
4523         chunk_reset(out);
4524         memset(info, 0, sizeof(*info) * len);
4525
4526         info[INF_NAME]                           = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, PRODUCT_NAME);
4527         info[INF_VERSION]                        = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, haproxy_version);
4528         info[INF_BUILD_INFO]                     = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, haproxy_version);
4529         info[INF_RELEASE_DATE]                   = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, haproxy_date);
4530
4531         info[INF_NBTHREAD]                       = mkf_u32(FO_CONFIG|FS_SERVICE, global.nbthread);
4532         info[INF_NBPROC]                         = mkf_u32(FO_CONFIG|FS_SERVICE, 1);
4533         info[INF_PROCESS_NUM]                    = mkf_u32(FO_KEY, 1);
4534         info[INF_PID]                            = mkf_u32(FO_STATUS, pid);
4535
4536         info[INF_UPTIME]                         = mkf_str(FN_DURATION, chunk_newstr(out));
4537         chunk_appendf(out, "%ud %uh%02um%02us", (uint)up.tv_sec / 86400, ((uint)up.tv_sec % 86400) / 3600, ((uint)up.tv_sec % 3600) / 60, ((uint)up.tv_sec % 60));
4538
4539         info[INF_UPTIME_SEC]                     = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_DURATION, up.tv_sec + up.tv_usec / 1000000.0) : mkf_u32(FN_DURATION, up.tv_sec);
4540         info[INF_START_TIME_SEC]                 = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_DURATION, start_date.tv_sec + start_date.tv_usec / 1000000.0) : mkf_u32(FN_DURATION, start_date.tv_sec);
4541         info[INF_MEMMAX_MB]                      = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_memmax);
4542         info[INF_MEMMAX_BYTES]                   = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_memmax * 1048576L);
4543         info[INF_POOL_ALLOC_MB]                  = mkf_u32(0, (unsigned)(pool_total_allocated() / 1048576L));
4544         info[INF_POOL_ALLOC_BYTES]               = mkf_u64(0, pool_total_allocated());
4545         info[INF_POOL_USED_MB]                   = mkf_u32(0, (unsigned)(pool_total_used() / 1048576L));
4546         info[INF_POOL_USED_BYTES]                = mkf_u64(0, pool_total_used());
4547         info[INF_POOL_FAILED]                    = mkf_u32(FN_COUNTER, pool_total_failures());
4548         info[INF_ULIMIT_N]                       = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_nofile);
4549         info[INF_MAXSOCK]                        = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxsock);
4550         info[INF_MAXCONN]                        = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxconn);
4551         info[INF_HARD_MAXCONN]                   = mkf_u32(FO_CONFIG|FN_LIMIT, global.hardmaxconn);
4552         info[INF_CURR_CONN]                      = mkf_u32(0, actconn);
4553         info[INF_CUM_CONN]                       = mkf_u32(FN_COUNTER, totalconn);
4554         info[INF_CUM_REQ]                        = mkf_u32(FN_COUNTER, global.req_count);
4555 #ifdef USE_OPENSSL
4556         info[INF_MAX_SSL_CONNS]                  = mkf_u32(FN_MAX, global.maxsslconn);
4557         info[INF_CURR_SSL_CONNS]                 = mkf_u32(0, global.sslconns);
4558         info[INF_CUM_SSL_CONNS]                  = mkf_u32(FN_COUNTER, global.totalsslconns);
4559 #endif
4560         info[INF_MAXPIPES]                       = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxpipes);
4561         info[INF_PIPES_USED]                     = mkf_u32(0, pipes_used);
4562         info[INF_PIPES_FREE]                     = mkf_u32(0, pipes_free);
4563         info[INF_CONN_RATE]                      = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, read_freq_ctr_flt(&global.conn_per_sec)) : mkf_u32(FN_RATE, read_freq_ctr(&global.conn_per_sec));
4564         info[INF_CONN_RATE_LIMIT]                = mkf_u32(FO_CONFIG|FN_LIMIT, global.cps_lim);
4565         info[INF_MAX_CONN_RATE]                  = mkf_u32(FN_MAX, global.cps_max);
4566         info[INF_SESS_RATE]                      = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, read_freq_ctr_flt(&global.sess_per_sec)) : mkf_u32(FN_RATE, read_freq_ctr(&global.sess_per_sec));
4567         info[INF_SESS_RATE_LIMIT]                = mkf_u32(FO_CONFIG|FN_LIMIT, global.sps_lim);
4568         info[INF_MAX_SESS_RATE]                  = mkf_u32(FN_RATE, global.sps_max);
4569
4570 #ifdef USE_OPENSSL
4571         info[INF_SSL_RATE]                       = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, ssl_sess_rate) : mkf_u32(FN_RATE, ssl_sess_rate);
4572         info[INF_SSL_RATE_LIMIT]                 = mkf_u32(FO_CONFIG|FN_LIMIT, global.ssl_lim);
4573         info[INF_MAX_SSL_RATE]                   = mkf_u32(FN_MAX, global.ssl_max);
4574         info[INF_SSL_FRONTEND_KEY_RATE]          = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, ssl_key_rate) : mkf_u32(0, ssl_key_rate);
4575         info[INF_SSL_FRONTEND_MAX_KEY_RATE]      = mkf_u32(FN_MAX, global.ssl_fe_keys_max);
4576         info[INF_SSL_FRONTEND_SESSION_REUSE_PCT] = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, ssl_reuse) : mkf_u32(0, ssl_reuse);
4577         info[INF_SSL_BACKEND_KEY_RATE]           = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, read_freq_ctr_flt(&global.ssl_be_keys_per_sec)) : mkf_u32(FN_RATE, read_freq_ctr(&global.ssl_be_keys_per_sec));
4578         info[INF_SSL_BACKEND_MAX_KEY_RATE]       = mkf_u32(FN_MAX, global.ssl_be_keys_max);
4579         info[INF_SSL_CACHE_LOOKUPS]              = mkf_u32(FN_COUNTER, global.shctx_lookups);
4580         info[INF_SSL_CACHE_MISSES]               = mkf_u32(FN_COUNTER, global.shctx_misses);
4581 #endif
4582         info[INF_COMPRESS_BPS_IN]                = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, read_freq_ctr_flt(&global.comp_bps_in)) : mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_in));
4583         info[INF_COMPRESS_BPS_OUT]               = (flags & STAT_USE_FLOAT) ? mkf_flt(FN_RATE, read_freq_ctr_flt(&global.comp_bps_out)) : mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_out));
4584         info[INF_COMPRESS_BPS_RATE_LIM]          = mkf_u32(FO_CONFIG|FN_LIMIT, global.comp_rate_lim);
4585 #ifdef USE_ZLIB
4586         info[INF_ZLIB_MEM_USAGE]                 = mkf_u32(0, zlib_used_memory);
4587         info[INF_MAX_ZLIB_MEM_USAGE]             = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxzlibmem);
4588 #endif
4589         info[INF_TASKS]                          = mkf_u32(0, total_allocated_tasks());
4590         info[INF_RUN_QUEUE]                      = mkf_u32(0, total_run_queues());
4591         info[INF_IDLE_PCT]                       = mkf_u32(FN_AVG, clock_report_idle());
4592         info[INF_NODE]                           = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.node);
4593         if (global.desc)
4594                 info[INF_DESCRIPTION]            = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.desc);
4595         info[INF_STOPPING]                       = mkf_u32(0, stopping);
4596         info[INF_JOBS]                           = mkf_u32(0, jobs);
4597         info[INF_UNSTOPPABLE_JOBS]               = mkf_u32(0, unstoppable_jobs);
4598         info[INF_LISTENERS]                      = mkf_u32(0, listeners);
4599         info[INF_ACTIVE_PEERS]                   = mkf_u32(0, active_peers);
4600         info[INF_CONNECTED_PEERS]                = mkf_u32(0, connected_peers);
4601         info[INF_DROPPED_LOGS]                   = mkf_u32(0, dropped_logs);
4602         info[INF_BUSY_POLLING]                   = mkf_u32(0, !!(global.tune.options & GTUNE_BUSY_POLLING));
4603         info[INF_FAILED_RESOLUTIONS]             = mkf_u32(0, resolv_failed_resolutions);
4604         info[INF_TOTAL_BYTES_OUT]                = mkf_u64(0, global.out_bytes);
4605         info[INF_TOTAL_SPLICED_BYTES_OUT]        = mkf_u64(0, global.spliced_out_bytes);
4606         info[INF_BYTES_OUT_RATE]                 = mkf_u64(FN_RATE, (unsigned long long)read_freq_ctr(&global.out_32bps) * 32);
4607         info[INF_DEBUG_COMMANDS_ISSUED]          = mkf_u32(0, debug_commands_issued);
4608         info[INF_CUM_LOG_MSGS]                   = mkf_u32(FN_COUNTER, cum_log_messages);
4609
4610         info[INF_TAINTED]                        = mkf_str(FO_STATUS, chunk_newstr(out));
4611         chunk_appendf(out, "%#x", get_tainted());
4612
4613         return 1;
4614 }
4615
4616 /* This function dumps information onto the stream connector's read buffer.
4617  * It returns 0 as long as it does not complete, non-zero upon completion.
4618  * No state is used.
4619  */
4620 static int stats_dump_info_to_buffer(struct stconn *sc)
4621 {
4622         struct appctx *appctx = __sc_appctx(sc);
4623         struct show_stat_ctx *ctx = appctx->svcctx;
4624         int ret;
4625         int current_field;
4626
4627         if (!stats_fill_info(info, INF_TOTAL_FIELDS, ctx->flags))
4628                 return 0;
4629
4630         chunk_reset(&trash_chunk);
4631 more:
4632         current_field = ctx->field;
4633
4634         if (ctx->flags & STAT_FMT_TYPED)
4635                 ret = stats_dump_typed_info_fields(&trash_chunk, info, ctx);
4636         else if (ctx->flags & STAT_FMT_JSON)
4637                 ret = stats_dump_json_info_fields(&trash_chunk, info, ctx);
4638         else
4639                 ret = stats_dump_info_fields(&trash_chunk, info, ctx);
4640
4641         if (applet_putchk(appctx, &trash_chunk) == -1) {
4642                 /* restore previous field */
4643                 ctx->field = current_field;
4644                 return 0;
4645         }
4646         if (ret && ctx->field) {
4647                 /* partial dump */
4648                 goto more;
4649         }
4650         ctx->field = 0;
4651         return 1;
4652 }
4653
4654 /* This function dumps the schema onto the stream connector's read buffer.
4655  * It returns 0 as long as it does not complete, non-zero upon completion.
4656  * No state is used.
4657  *
4658  * Integer values bounded to the range [-(2**53)+1, (2**53)-1] as
4659  * per the recommendation for interoperable integers in section 6 of RFC 7159.
4660  */
4661 static void stats_dump_json_schema(struct buffer *out)
4662 {
4663
4664         int old_len = out->data;
4665
4666         chunk_strcat(out,
4667                      "{"
4668                       "\"$schema\":\"http://json-schema.org/draft-04/schema#\","
4669                       "\"oneOf\":["
4670                        "{"
4671                         "\"title\":\"Info\","
4672                         "\"type\":\"array\","
4673                         "\"items\":{"
4674                          "\"title\":\"InfoItem\","
4675                          "\"type\":\"object\","
4676                          "\"properties\":{"
4677                           "\"field\":{\"$ref\":\"#/definitions/field\"},"
4678                           "\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
4679                           "\"tags\":{\"$ref\":\"#/definitions/tags\"},"
4680                           "\"value\":{\"$ref\":\"#/definitions/typedValue\"}"
4681                          "},"
4682                          "\"required\":[\"field\",\"processNum\",\"tags\","
4683                                        "\"value\"]"
4684                         "}"
4685                        "},"
4686                        "{"
4687                         "\"title\":\"Stat\","
4688                         "\"type\":\"array\","
4689                         "\"items\":{"
4690                          "\"title\":\"InfoItem\","
4691                          "\"type\":\"object\","
4692                          "\"properties\":{"
4693                           "\"objType\":{"
4694                            "\"enum\":[\"Frontend\",\"Backend\",\"Listener\","
4695                                      "\"Server\",\"Unknown\"]"
4696                           "},"
4697                           "\"proxyId\":{"
4698                            "\"type\":\"integer\","
4699                            "\"minimum\":0"
4700                           "},"
4701                           "\"id\":{"
4702                            "\"type\":\"integer\","
4703                            "\"minimum\":0"
4704                           "},"
4705                           "\"field\":{\"$ref\":\"#/definitions/field\"},"
4706                           "\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
4707                           "\"tags\":{\"$ref\":\"#/definitions/tags\"},"
4708                           "\"typedValue\":{\"$ref\":\"#/definitions/typedValue\"}"
4709                          "},"
4710                          "\"required\":[\"objType\",\"proxyId\",\"id\","
4711                                        "\"field\",\"processNum\",\"tags\","
4712                                        "\"value\"]"
4713                         "}"
4714                        "},"
4715                        "{"
4716                         "\"title\":\"Error\","
4717                         "\"type\":\"object\","
4718                         "\"properties\":{"
4719                          "\"errorStr\":{"
4720                           "\"type\":\"string\""
4721                          "}"
4722                         "},"
4723                         "\"required\":[\"errorStr\"]"
4724                        "}"
4725                       "],"
4726                       "\"definitions\":{"
4727                        "\"field\":{"
4728                         "\"type\":\"object\","
4729                         "\"pos\":{"
4730                          "\"type\":\"integer\","
4731                          "\"minimum\":0"
4732                         "},"
4733                         "\"name\":{"
4734                          "\"type\":\"string\""
4735                         "},"
4736                         "\"required\":[\"pos\",\"name\"]"
4737                        "},"
4738                        "\"processNum\":{"
4739                         "\"type\":\"integer\","
4740                         "\"minimum\":1"
4741                        "},"
4742                        "\"tags\":{"
4743                         "\"type\":\"object\","
4744                         "\"origin\":{"
4745                          "\"type\":\"string\","
4746                          "\"enum\":[\"Metric\",\"Status\",\"Key\","
4747                                    "\"Config\",\"Product\",\"Unknown\"]"
4748                         "},"
4749                         "\"nature\":{"
4750                          "\"type\":\"string\","
4751                          "\"enum\":[\"Gauge\",\"Limit\",\"Min\",\"Max\","
4752                                    "\"Rate\",\"Counter\",\"Duration\","
4753                                    "\"Age\",\"Time\",\"Name\",\"Output\","
4754                                    "\"Avg\", \"Unknown\"]"
4755                         "},"
4756                         "\"scope\":{"
4757                          "\"type\":\"string\","
4758                          "\"enum\":[\"Cluster\",\"Process\",\"Service\","
4759                                    "\"System\",\"Unknown\"]"
4760                         "},"
4761                         "\"required\":[\"origin\",\"nature\",\"scope\"]"
4762                        "},"
4763                        "\"typedValue\":{"
4764                         "\"type\":\"object\","
4765                         "\"oneOf\":["
4766                          "{\"$ref\":\"#/definitions/typedValue/definitions/s32Value\"},"
4767                          "{\"$ref\":\"#/definitions/typedValue/definitions/s64Value\"},"
4768                          "{\"$ref\":\"#/definitions/typedValue/definitions/u32Value\"},"
4769                          "{\"$ref\":\"#/definitions/typedValue/definitions/u64Value\"},"
4770                          "{\"$ref\":\"#/definitions/typedValue/definitions/strValue\"}"
4771                         "],"
4772                         "\"definitions\":{"
4773                          "\"s32Value\":{"
4774                           "\"properties\":{"
4775                            "\"type\":{"
4776                             "\"type\":\"string\","
4777                             "\"enum\":[\"s32\"]"
4778                            "},"
4779                            "\"value\":{"
4780                             "\"type\":\"integer\","
4781                             "\"minimum\":-2147483648,"
4782                             "\"maximum\":2147483647"
4783                            "}"
4784                           "},"
4785                           "\"required\":[\"type\",\"value\"]"
4786                          "},"
4787                          "\"s64Value\":{"
4788                           "\"properties\":{"
4789                            "\"type\":{"
4790                             "\"type\":\"string\","
4791                             "\"enum\":[\"s64\"]"
4792                            "},"
4793                            "\"value\":{"
4794                             "\"type\":\"integer\","
4795                             "\"minimum\":-9007199254740991,"
4796                             "\"maximum\":9007199254740991"
4797                            "}"
4798                           "},"
4799                           "\"required\":[\"type\",\"value\"]"
4800                          "},"
4801                          "\"u32Value\":{"
4802                           "\"properties\":{"
4803                            "\"type\":{"
4804                             "\"type\":\"string\","
4805                             "\"enum\":[\"u32\"]"
4806                            "},"
4807                            "\"value\":{"
4808                             "\"type\":\"integer\","
4809                             "\"minimum\":0,"
4810                             "\"maximum\":4294967295"
4811                            "}"
4812                           "},"
4813                           "\"required\":[\"type\",\"value\"]"
4814                          "},"
4815                          "\"u64Value\":{"
4816                           "\"properties\":{"
4817                            "\"type\":{"
4818                             "\"type\":\"string\","
4819                             "\"enum\":[\"u64\"]"
4820                            "},"
4821                            "\"value\":{"
4822                             "\"type\":\"integer\","
4823                             "\"minimum\":0,"
4824                             "\"maximum\":9007199254740991"
4825                            "}"
4826                           "},"
4827                           "\"required\":[\"type\",\"value\"]"
4828                          "},"
4829                          "\"strValue\":{"
4830                           "\"properties\":{"
4831                            "\"type\":{"
4832                             "\"type\":\"string\","
4833                             "\"enum\":[\"str\"]"
4834                            "},"
4835                            "\"value\":{\"type\":\"string\"}"
4836                           "},"
4837                           "\"required\":[\"type\",\"value\"]"
4838                          "},"
4839                          "\"unknownValue\":{"
4840                           "\"properties\":{"
4841                            "\"type\":{"
4842                             "\"type\":\"integer\","
4843                             "\"minimum\":0"
4844                            "},"
4845                            "\"value\":{"
4846                             "\"type\":\"string\","
4847                             "\"enum\":[\"unknown\"]"
4848                            "}"
4849                           "},"
4850                           "\"required\":[\"type\",\"value\"]"
4851                          "}"
4852                         "}"
4853                        "}"
4854                       "}"
4855                      "}");
4856
4857         if (old_len == out->data) {
4858                 chunk_reset(out);
4859                 chunk_appendf(out,
4860                               "{\"errorStr\":\"output buffer too short\"}");
4861         }
4862         chunk_appendf(out, "\n");
4863 }
4864
4865 /* This function dumps the schema onto the stream connector's read buffer.
4866  * It returns 0 as long as it does not complete, non-zero upon completion.
4867  * No state is used.
4868  */
4869 static int stats_dump_json_schema_to_buffer(struct appctx *appctx)
4870 {
4871
4872         chunk_reset(&trash_chunk);
4873
4874         stats_dump_json_schema(&trash_chunk);
4875
4876         if (applet_putchk(appctx, &trash_chunk) == -1)
4877                 return 0;
4878
4879         return 1;
4880 }
4881
4882 static int cli_parse_clear_counters(char **args, char *payload, struct appctx *appctx, void *private)
4883 {
4884         struct proxy *px;
4885         struct server *sv;
4886         struct listener *li;
4887         struct stats_module *mod;
4888         int clrall = 0;
4889
4890         if (strcmp(args[2], "all") == 0)
4891                 clrall = 1;
4892
4893         /* check permissions */
4894         if (!cli_has_level(appctx, ACCESS_LVL_OPER) ||
4895             (clrall && !cli_has_level(appctx, ACCESS_LVL_ADMIN)))
4896                 return 1;
4897
4898         for (px = proxies_list; px; px = px->next) {
4899                 if (clrall) {
4900                         memset(&px->be_counters, 0, sizeof(px->be_counters));
4901                         memset(&px->fe_counters, 0, sizeof(px->fe_counters));
4902                 }
4903                 else {
4904                         px->be_counters.conn_max = 0;
4905                         px->be_counters.p.http.rps_max = 0;
4906                         px->be_counters.sps_max = 0;
4907                         px->be_counters.cps_max = 0;
4908                         px->be_counters.nbpend_max = 0;
4909                         px->be_counters.qtime_max = 0;
4910                         px->be_counters.ctime_max = 0;
4911                         px->be_counters.dtime_max = 0;
4912                         px->be_counters.ttime_max = 0;
4913
4914                         px->fe_counters.conn_max = 0;
4915                         px->fe_counters.p.http.rps_max = 0;
4916                         px->fe_counters.sps_max = 0;
4917                         px->fe_counters.cps_max = 0;
4918                 }
4919
4920                 for (sv = px->srv; sv; sv = sv->next)
4921                         if (clrall)
4922                                 memset(&sv->counters, 0, sizeof(sv->counters));
4923                         else {
4924                                 sv->counters.cur_sess_max = 0;
4925                                 sv->counters.nbpend_max = 0;
4926                                 sv->counters.sps_max = 0;
4927                                 sv->counters.qtime_max = 0;
4928                                 sv->counters.ctime_max = 0;
4929                                 sv->counters.dtime_max = 0;
4930                                 sv->counters.ttime_max = 0;
4931                         }
4932
4933                 list_for_each_entry(li, &px->conf.listeners, by_fe)
4934                         if (li->counters) {
4935                                 if (clrall)
4936                                         memset(li->counters, 0, sizeof(*li->counters));
4937                                 else
4938                                         li->counters->conn_max = 0;
4939                         }
4940         }
4941
4942         global.cps_max = 0;
4943         global.sps_max = 0;
4944         global.ssl_max = 0;
4945         global.ssl_fe_keys_max = 0;
4946         global.ssl_be_keys_max = 0;
4947
4948         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
4949                 if (!mod->clearable && !clrall)
4950                         continue;
4951
4952                 for (px = proxies_list; px; px = px->next) {
4953                         enum stats_domain_px_cap mod_cap = stats_px_get_cap(mod->domain_flags);
4954
4955                         if (px->cap & PR_CAP_FE && mod_cap & STATS_PX_CAP_FE) {
4956                                 EXTRA_COUNTERS_INIT(px->extra_counters_fe,
4957                                                     mod,
4958                                                     mod->counters,
4959                                                     mod->counters_size);
4960                         }
4961
4962                         if (px->cap & PR_CAP_BE && mod_cap & STATS_PX_CAP_BE) {
4963                                 EXTRA_COUNTERS_INIT(px->extra_counters_be,
4964                                                     mod,
4965                                                     mod->counters,
4966                                                     mod->counters_size);
4967                         }
4968
4969                         if (mod_cap & STATS_PX_CAP_SRV) {
4970                                 for (sv = px->srv; sv; sv = sv->next) {
4971                                         EXTRA_COUNTERS_INIT(sv->extra_counters,
4972                                                             mod,
4973                                                             mod->counters,
4974                                                             mod->counters_size);
4975                                 }
4976                         }
4977
4978                         if (mod_cap & STATS_PX_CAP_LI) {
4979                                 list_for_each_entry(li, &px->conf.listeners, by_fe) {
4980                                         EXTRA_COUNTERS_INIT(li->extra_counters,
4981                                                             mod,
4982                                                             mod->counters,
4983                                                             mod->counters_size);
4984                                 }
4985                         }
4986                 }
4987         }
4988
4989         resolv_stats_clear_counters(clrall, &stats_module_list[STATS_DOMAIN_RESOLVERS]);
4990
4991         memset(activity, 0, sizeof(activity));
4992         return 1;
4993 }
4994
4995
4996 static int cli_parse_show_info(char **args, char *payload, struct appctx *appctx, void *private)
4997 {
4998         struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
4999         int arg = 2;
5000
5001         ctx->scope_str = 0;
5002         ctx->scope_len = 0;
5003         ctx->flags = 0;
5004         ctx->field = 0; /* explicit default value */
5005
5006         while (*args[arg]) {
5007                 if (strcmp(args[arg], "typed") == 0)
5008                         ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_TYPED;
5009                 else if (strcmp(args[arg], "json") == 0)
5010                         ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_JSON;
5011                 else if (strcmp(args[arg], "desc") == 0)
5012                         ctx->flags |= STAT_SHOW_FDESC;
5013                 else if (strcmp(args[arg], "float") == 0)
5014                         ctx->flags |= STAT_USE_FLOAT;
5015                 arg++;
5016         }
5017         return 0;
5018 }
5019
5020
5021 static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx, void *private)
5022 {
5023         struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
5024         int arg = 2;
5025
5026         ctx->scope_str = 0;
5027         ctx->scope_len = 0;
5028         ctx->flags = STAT_SHNODE | STAT_SHDESC;
5029
5030         if ((strm_li(appctx_strm(appctx))->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER)
5031                 ctx->flags |= STAT_SHLGNDS;
5032
5033         /* proxy is the default domain */
5034         ctx->domain = STATS_DOMAIN_PROXY;
5035         if (strcmp(args[arg], "domain") == 0) {
5036                 ++args;
5037
5038                 if (strcmp(args[arg], "proxy") == 0) {
5039                         ++args;
5040                 } else if (strcmp(args[arg], "resolvers") == 0) {
5041                         ctx->domain = STATS_DOMAIN_RESOLVERS;
5042                         ++args;
5043                 } else {
5044                         return cli_err(appctx, "Invalid statistics domain.\n");
5045                 }
5046         }
5047
5048         if (ctx->domain == STATS_DOMAIN_PROXY
5049             && *args[arg] && *args[arg+1] && *args[arg+2]) {
5050                 struct proxy *px;
5051
5052                 px = proxy_find_by_name(args[arg], 0, 0);
5053                 if (px)
5054                         ctx->iid = px->uuid;
5055                 else
5056                         ctx->iid = atoi(args[arg]);
5057
5058                 if (!ctx->iid)
5059                         return cli_err(appctx, "No such proxy.\n");
5060
5061                 ctx->flags |= STAT_BOUND;
5062                 ctx->type = atoi(args[arg+1]);
5063                 ctx->sid = atoi(args[arg+2]);
5064                 arg += 3;
5065         }
5066
5067         while (*args[arg]) {
5068                 if (strcmp(args[arg], "typed") == 0)
5069                         ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_TYPED;
5070                 else if (strcmp(args[arg], "json") == 0)
5071                         ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_JSON;
5072                 else if (strcmp(args[arg], "desc") == 0)
5073                         ctx->flags |= STAT_SHOW_FDESC;
5074                 else if (strcmp(args[arg], "no-maint") == 0)
5075                         ctx->flags |= STAT_HIDE_MAINT;
5076                 else if (strcmp(args[arg], "up") == 0)
5077                         ctx->flags |= STAT_HIDE_DOWN;
5078                 arg++;
5079         }
5080
5081         return 0;
5082 }
5083
5084 static int cli_io_handler_dump_info(struct appctx *appctx)
5085 {
5086         trash_chunk = b_make(trash.area, trash.size, 0, 0);
5087         return stats_dump_info_to_buffer(appctx_sc(appctx));
5088 }
5089
5090 /* This I/O handler runs as an applet embedded in a stream connector. It is
5091  * used to send raw stats over a socket.
5092  */
5093 static int cli_io_handler_dump_stat(struct appctx *appctx)
5094 {
5095         trash_chunk = b_make(trash.area, trash.size, 0, 0);
5096         return stats_dump_stat_to_buffer(appctx_sc(appctx), NULL, NULL);
5097 }
5098
5099 static int cli_io_handler_dump_json_schema(struct appctx *appctx)
5100 {
5101         trash_chunk = b_make(trash.area, trash.size, 0, 0);
5102         return stats_dump_json_schema_to_buffer(appctx);
5103 }
5104
5105 int stats_allocate_proxy_counters_internal(struct extra_counters **counters,
5106                                            int type, int px_cap)
5107 {
5108         struct stats_module *mod;
5109
5110         EXTRA_COUNTERS_REGISTER(counters, type, alloc_failed);
5111
5112         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
5113                 if (!(stats_px_get_cap(mod->domain_flags) & px_cap))
5114                         continue;
5115
5116                 EXTRA_COUNTERS_ADD(mod, *counters, mod->counters, mod->counters_size);
5117         }
5118
5119         EXTRA_COUNTERS_ALLOC(*counters, alloc_failed);
5120
5121         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
5122                 if (!(stats_px_get_cap(mod->domain_flags) & px_cap))
5123                         continue;
5124
5125                 EXTRA_COUNTERS_INIT(*counters, mod, mod->counters, mod->counters_size);
5126         }
5127
5128         return 1;
5129
5130   alloc_failed:
5131         return 0;
5132 }
5133
5134 /* Initialize and allocate all extra counters for a proxy and its attached
5135  * servers/listeners with all already registered stats module
5136  */
5137 int stats_allocate_proxy_counters(struct proxy *px)
5138 {
5139         struct server *sv;
5140         struct listener *li;
5141
5142         if (px->cap & PR_CAP_FE) {
5143                 if (!stats_allocate_proxy_counters_internal(&px->extra_counters_fe,
5144                                                             COUNTERS_FE,
5145                                                             STATS_PX_CAP_FE)) {
5146                         return 0;
5147                 }
5148         }
5149
5150         if (px->cap & PR_CAP_BE) {
5151                 if (!stats_allocate_proxy_counters_internal(&px->extra_counters_be,
5152                                                             COUNTERS_BE,
5153                                                             STATS_PX_CAP_BE)) {
5154                         return 0;
5155                 }
5156         }
5157
5158         for (sv = px->srv; sv; sv = sv->next) {
5159                 if (!stats_allocate_proxy_counters_internal(&sv->extra_counters,
5160                                                             COUNTERS_SV,
5161                                                             STATS_PX_CAP_SRV)) {
5162                         return 0;
5163                 }
5164         }
5165
5166         list_for_each_entry(li, &px->conf.listeners, by_fe) {
5167                 if (!stats_allocate_proxy_counters_internal(&li->extra_counters,
5168                                                             COUNTERS_LI,
5169                                                             STATS_PX_CAP_LI)) {
5170                         return 0;
5171                 }
5172         }
5173
5174         return 1;
5175 }
5176
5177 void stats_register_module(struct stats_module *m)
5178 {
5179         const uint8_t domain = stats_get_domain(m->domain_flags);
5180
5181         LIST_APPEND(&stats_module_list[domain], &m->list);
5182         stat_count[domain] += m->stats_count;
5183 }
5184
5185 static int allocate_stats_px_postcheck(void)
5186 {
5187         struct stats_module *mod;
5188         size_t i = ST_F_TOTAL_FIELDS;
5189         int err_code = 0;
5190         struct proxy *px;
5191
5192         stat_count[STATS_DOMAIN_PROXY] += ST_F_TOTAL_FIELDS;
5193
5194         stat_f[STATS_DOMAIN_PROXY] = malloc(stat_count[STATS_DOMAIN_PROXY] * sizeof(struct name_desc));
5195         if (!stat_f[STATS_DOMAIN_PROXY]) {
5196                 ha_alert("stats: cannot allocate all fields for proxy statistics\n");
5197                 err_code |= ERR_ALERT | ERR_FATAL;
5198                 return err_code;
5199         }
5200
5201         memcpy(stat_f[STATS_DOMAIN_PROXY], stat_fields,
5202                ST_F_TOTAL_FIELDS * sizeof(struct name_desc));
5203
5204         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
5205                 memcpy(stat_f[STATS_DOMAIN_PROXY] + i,
5206                        mod->stats,
5207                        mod->stats_count * sizeof(struct name_desc));
5208                 i += mod->stats_count;
5209         }
5210
5211         for (px = proxies_list; px; px = px->next) {
5212                 if (!stats_allocate_proxy_counters(px)) {
5213                         ha_alert("stats: cannot allocate all counters for proxy statistics\n");
5214                         err_code |= ERR_ALERT | ERR_FATAL;
5215                         return err_code;
5216                 }
5217         }
5218
5219         /* wait per-thread alloc to perform corresponding stat_l allocation */
5220
5221         return err_code;
5222 }
5223
5224 REGISTER_CONFIG_POSTPARSER("allocate-stats-px", allocate_stats_px_postcheck);
5225
5226 static int allocate_stats_rslv_postcheck(void)
5227 {
5228         struct stats_module *mod;
5229         size_t i = 0;
5230         int err_code = 0;
5231
5232         stat_f[STATS_DOMAIN_RESOLVERS] = malloc(stat_count[STATS_DOMAIN_RESOLVERS] * sizeof(struct name_desc));
5233         if (!stat_f[STATS_DOMAIN_RESOLVERS]) {
5234                 ha_alert("stats: cannot allocate all fields for resolver statistics\n");
5235                 err_code |= ERR_ALERT | ERR_FATAL;
5236                 return err_code;
5237         }
5238
5239         list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_RESOLVERS], list) {
5240                 memcpy(stat_f[STATS_DOMAIN_RESOLVERS] + i,
5241                        mod->stats,
5242                        mod->stats_count * sizeof(struct name_desc));
5243                 i += mod->stats_count;
5244         }
5245
5246         if (!resolv_allocate_counters(&stats_module_list[STATS_DOMAIN_RESOLVERS])) {
5247                 ha_alert("stats: cannot allocate all counters for resolver statistics\n");
5248                 err_code |= ERR_ALERT | ERR_FATAL;
5249                 return err_code;
5250         }
5251
5252         /* wait per-thread alloc to perform corresponding stat_l allocation */
5253
5254         return err_code;
5255 }
5256
5257 REGISTER_CONFIG_POSTPARSER("allocate-stats-resolver", allocate_stats_rslv_postcheck);
5258
5259 static int allocate_stat_lines_per_thread(void)
5260 {
5261         int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_RESOLVERS }, i;
5262
5263         for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
5264                 const int domain = domains[i];
5265
5266                 stat_l[domain] = malloc(stat_count[domain] * sizeof(struct field));
5267                 if (!stat_l[domain])
5268                         return 0;
5269         }
5270         return 1;
5271 }
5272
5273 REGISTER_PER_THREAD_ALLOC(allocate_stat_lines_per_thread);
5274
5275 static int allocate_trash_counters(void)
5276 {
5277         struct stats_module *mod;
5278         int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_RESOLVERS }, i;
5279         size_t max_counters_size = 0;
5280
5281         /* calculate the greatest counters used by any stats modules */
5282         for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
5283                 list_for_each_entry(mod, &stats_module_list[domains[i]], list) {
5284                         max_counters_size = mod->counters_size > max_counters_size ?
5285                                             mod->counters_size : max_counters_size;
5286                 }
5287         }
5288
5289         /* allocate the trash with the size of the greatest counters */
5290         if (max_counters_size) {
5291                 trash_counters = malloc(max_counters_size);
5292                 if (!trash_counters) {
5293                         ha_alert("stats: cannot allocate trash counters for statistics\n");
5294                         return 0;
5295                 }
5296         }
5297
5298         return 1;
5299 }
5300
5301 REGISTER_PER_THREAD_ALLOC(allocate_trash_counters);
5302
5303 static void deinit_stat_lines_per_thread(void)
5304 {
5305         int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_RESOLVERS }, i;
5306
5307         for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
5308                 const int domain = domains[i];
5309
5310                 ha_free(&stat_l[domain]);
5311         }
5312 }
5313
5314
5315 REGISTER_PER_THREAD_FREE(deinit_stat_lines_per_thread);
5316
5317 static void deinit_stats(void)
5318 {
5319         int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_RESOLVERS }, i;
5320
5321         for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
5322                 const int domain = domains[i];
5323
5324                 if (stat_f[domain])
5325                         free(stat_f[domain]);
5326         }
5327 }
5328
5329 REGISTER_POST_DEINIT(deinit_stats);
5330
5331 static void free_trash_counters(void)
5332 {
5333         if (trash_counters)
5334                 free(trash_counters);
5335 }
5336
5337 REGISTER_PER_THREAD_FREE(free_trash_counters);
5338
5339 /* register cli keywords */
5340 static struct cli_kw_list cli_kws = {{ },{
5341         { { "clear", "counters",  NULL },      "clear counters [all]                    : clear max statistics counters (or all counters)", cli_parse_clear_counters, NULL, NULL },
5342         { { "show", "info",  NULL },           "show info [desc|json|typed|float]*      : report information about the running process",    cli_parse_show_info, cli_io_handler_dump_info, NULL },
5343         { { "show", "stat",  NULL },           "show stat [desc|json|no-maint|typed|up]*: report counters for each proxy and server",       cli_parse_show_stat, cli_io_handler_dump_stat, NULL },
5344         { { "show", "schema",  "json", NULL }, "show schema json                        : report schema used for stats",                    NULL, cli_io_handler_dump_json_schema, NULL },
5345         {{},}
5346 }};
5347
5348 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
5349
5350 struct applet http_stats_applet = {
5351         .obj_type = OBJ_TYPE_APPLET,
5352         .name = "<STATS>", /* used for logging */
5353         .fct = http_stats_io_handler,
5354         .release = NULL,
5355 };
5356
5357 /*
5358  * Local variables:
5359  *  c-indent-level: 8
5360  *  c-basic-offset: 8
5361  * End:
5362  */