Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Proxy variables and functions. |
3 | | * |
4 | | * Copyright 2000-2009 Willy Tarreau <w@1wt.eu> |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU General Public License |
8 | | * as published by the Free Software Foundation; either version |
9 | | * 2 of the License, or (at your option) any later version. |
10 | | * |
11 | | */ |
12 | | |
13 | | #include <unistd.h> |
14 | | #include <string.h> |
15 | | #include <sys/types.h> |
16 | | #include <sys/socket.h> |
17 | | #include <sys/stat.h> |
18 | | |
19 | | #include <import/eb32tree.h> |
20 | | #include <import/ebistree.h> |
21 | | |
22 | | #include <haproxy/acl.h> |
23 | | #include <haproxy/api.h> |
24 | | #include <haproxy/applet.h> |
25 | | #include <haproxy/capture-t.h> |
26 | | #include <haproxy/cfgparse.h> |
27 | | #include <haproxy/cli.h> |
28 | | #include <haproxy/counters.h> |
29 | | #include <haproxy/errors.h> |
30 | | #include <haproxy/fd.h> |
31 | | #include <haproxy/filters.h> |
32 | | #include <haproxy/global.h> |
33 | | #include <haproxy/guid.h> |
34 | | #include <haproxy/http_ana.h> |
35 | | #include <haproxy/http_htx.h> |
36 | | #include <haproxy/http_ext.h> |
37 | | #include <haproxy/http_rules.h> |
38 | | #include <haproxy/mailers.h> |
39 | | #include <haproxy/listener.h> |
40 | | #include <haproxy/log.h> |
41 | | #include <haproxy/obj_type-t.h> |
42 | | #include <haproxy/peers.h> |
43 | | #include <haproxy/pool.h> |
44 | | #include <haproxy/protocol.h> |
45 | | #include <haproxy/proto_tcp.h> |
46 | | #include <haproxy/proxy.h> |
47 | | #include <haproxy/sc_strm.h> |
48 | | #include <haproxy/quic_tp.h> |
49 | | #include <haproxy/server-t.h> |
50 | | #include <haproxy/signal.h> |
51 | | #include <haproxy/stats-t.h> |
52 | | #include <haproxy/stconn.h> |
53 | | #include <haproxy/stream.h> |
54 | | #include <haproxy/task.h> |
55 | | #include <haproxy/tcpcheck.h> |
56 | | #include <haproxy/time.h> |
57 | | #include <haproxy/tools.h> |
58 | | #include <haproxy/uri_auth.h> |
59 | | |
60 | | |
61 | | int listeners; /* # of proxy listeners, set by cfgparse */ |
62 | | struct proxy *proxies_list = NULL; /* list of main proxies */ |
63 | | struct list proxies = LIST_HEAD_INIT(proxies); /* list of all proxies */ |
64 | | struct eb_root used_proxy_id = EB_ROOT; /* list of proxy IDs in use */ |
65 | | struct eb_root proxy_by_name = EB_ROOT; /* tree of proxies sorted by name */ |
66 | | struct eb_root defproxy_by_name = EB_ROOT; /* tree of default proxies sorted by name (dups possible) */ |
67 | | struct proxy *orphaned_default_proxies = NULL; /* deleted ones with refcount != 0 */ |
68 | | unsigned int error_snapshot_id = 0; /* global ID assigned to each error then incremented */ |
69 | | |
70 | | /* CLI context used during "show servers {state|conn}" */ |
71 | | struct show_srv_ctx { |
72 | | struct proxy *px; /* current proxy to dump or NULL */ |
73 | | struct server *sv; /* current server to dump or NULL */ |
74 | | uint only_pxid; /* dump only this proxy ID when explicit */ |
75 | | int show_conn; /* non-zero = "conn" otherwise "state" */ |
76 | | enum { |
77 | | SHOW_SRV_HEAD = 0, |
78 | | SHOW_SRV_LIST, |
79 | | } state; |
80 | | }; |
81 | | |
82 | | /* proxy->options */ |
83 | | const struct cfg_opt cfg_opts[] = |
84 | | { |
85 | | { "abortonclose", PR_O_ABRT_CLOSE, PR_CAP_BE, 0, 0 }, |
86 | | { "allbackups", PR_O_USE_ALL_BK, PR_CAP_BE, 0, 0 }, |
87 | | { "checkcache", PR_O_CHK_CACHE, PR_CAP_BE, 0, PR_MODE_HTTP }, |
88 | | { "clitcpka", PR_O_TCP_CLI_KA, PR_CAP_FE, 0, 0 }, |
89 | | { "contstats", PR_O_CONTSTATS, PR_CAP_FE, 0, 0 }, |
90 | | { "dontlognull", PR_O_NULLNOLOG, PR_CAP_FE, 0, 0 }, |
91 | | { "http-buffer-request", PR_O_WREQ_BODY, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP }, |
92 | | { "http-drop-request-trailers", PR_O_HTTP_DROP_REQ_TRLS, PR_CAP_BE, 0, PR_MODE_HTTP }, |
93 | | { "http-drop-response-trailers", PR_O_HTTP_DROP_RES_TRLS, PR_CAP_FE, 0, PR_MODE_HTTP }, |
94 | | { "http-ignore-probes", PR_O_IGNORE_PRB, PR_CAP_FE, 0, PR_MODE_HTTP }, |
95 | | { "idle-close-on-response", PR_O_IDLE_CLOSE_RESP, PR_CAP_FE, 0, PR_MODE_HTTP }, |
96 | | { "prefer-last-server", PR_O_PREF_LAST, PR_CAP_BE, 0, PR_MODE_HTTP }, |
97 | | { "logasap", PR_O_LOGASAP, PR_CAP_FE, 0, 0 }, |
98 | | { "nolinger", PR_O_TCP_NOLING, PR_CAP_FE | PR_CAP_BE, 0, 0 }, |
99 | | { "persist", PR_O_PERSIST, PR_CAP_BE, 0, 0 }, |
100 | | { "srvtcpka", PR_O_TCP_SRV_KA, PR_CAP_BE, 0, 0 }, |
101 | | #ifdef USE_TPROXY |
102 | | { "transparent", PR_O_TRANSP, PR_CAP_BE, 0, 0 }, |
103 | | #else |
104 | | { "transparent", 0, 0, 0, 0 }, |
105 | | #endif |
106 | | |
107 | | { NULL, 0, 0, 0, 0 } |
108 | | }; |
109 | | |
110 | | /* proxy->options2 */ |
111 | | const struct cfg_opt cfg_opts2[] = |
112 | | { |
113 | | #ifdef USE_LINUX_SPLICE |
114 | | { "splice-request", PR_O2_SPLIC_REQ, PR_CAP_FE|PR_CAP_BE, 0, 0 }, |
115 | | { "splice-response", PR_O2_SPLIC_RTR, PR_CAP_FE|PR_CAP_BE, 0, 0 }, |
116 | | { "splice-auto", PR_O2_SPLIC_AUT, PR_CAP_FE|PR_CAP_BE, 0, 0 }, |
117 | | #else |
118 | | { "splice-request", 0, 0, 0, 0 }, |
119 | | { "splice-response", 0, 0, 0, 0 }, |
120 | | { "splice-auto", 0, 0, 0, 0 }, |
121 | | #endif |
122 | | { "accept-unsafe-violations-in-http-request", PR_O2_REQBUG_OK, PR_CAP_FE, 0, PR_MODE_HTTP }, |
123 | | { "accept-unsafe-violations-in-http-response", PR_O2_RSPBUG_OK, PR_CAP_BE, 0, PR_MODE_HTTP }, |
124 | | { "dontlog-normal", PR_O2_NOLOGNORM, PR_CAP_FE, 0, 0 }, |
125 | | { "log-separate-errors", PR_O2_LOGERRORS, PR_CAP_FE, 0, 0 }, |
126 | | { "log-health-checks", PR_O2_LOGHCHKS, PR_CAP_BE, 0, 0 }, |
127 | | { "socket-stats", PR_O2_SOCKSTAT, PR_CAP_FE, 0, 0 }, |
128 | | { "tcp-smart-accept", PR_O2_SMARTACC, PR_CAP_FE, 0, 0 }, |
129 | | { "tcp-smart-connect", PR_O2_SMARTCON, PR_CAP_BE, 0, 0 }, |
130 | | { "independent-streams", PR_O2_INDEPSTR, PR_CAP_FE|PR_CAP_BE, 0, 0 }, |
131 | | { "http-use-proxy-header", PR_O2_USE_PXHDR, PR_CAP_FE, 0, PR_MODE_HTTP }, |
132 | | { "http-pretend-keepalive", PR_O2_FAKE_KA, PR_CAP_BE, 0, PR_MODE_HTTP }, |
133 | | { "http-no-delay", PR_O2_NODELAY, PR_CAP_FE|PR_CAP_BE, 0, PR_MODE_HTTP }, |
134 | | |
135 | | {"h1-case-adjust-bogus-client", PR_O2_H1_ADJ_BUGCLI, PR_CAP_FE, 0, 0 }, |
136 | | {"h1-case-adjust-bogus-server", PR_O2_H1_ADJ_BUGSRV, PR_CAP_BE, 0, 0 }, |
137 | | {"disable-h2-upgrade", PR_O2_NO_H2_UPGRADE, PR_CAP_FE, 0, PR_MODE_HTTP }, |
138 | | { NULL, 0, 0, 0 } |
139 | | }; |
140 | | |
141 | | /* proxy->options3 */ |
142 | | const struct cfg_opt cfg_opts3[] = |
143 | | { |
144 | | {"assume-rfc6587-ntf", PR_O3_ASSUME_RFC6587_NTF, PR_CAP_FE, 0, PR_MODE_SYSLOG }, |
145 | | {"dont-parse-log", PR_O3_DONTPARSELOG, PR_CAP_FE, 0, PR_MODE_SYSLOG }, |
146 | | { NULL, 0, 0, 0 } |
147 | | }; |
148 | | |
149 | | /* Helper function to resolve a single sticking rule after config parsing. |
150 | | * Returns 1 for success and 0 for failure |
151 | | */ |
152 | | int resolve_stick_rule(struct proxy *curproxy, struct sticking_rule *mrule) |
153 | 0 | { |
154 | 0 | struct stktable *target; |
155 | |
|
156 | 0 | if (mrule->table.name) |
157 | 0 | target = stktable_find_by_name(mrule->table.name); |
158 | 0 | else |
159 | 0 | target = curproxy->table; |
160 | |
|
161 | 0 | if (!target) { |
162 | 0 | ha_alert("Proxy '%s': unable to find stick-table '%s'.\n", |
163 | 0 | curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id); |
164 | 0 | return 0; |
165 | 0 | } |
166 | 0 | else if (!stktable_compatible_sample(mrule->expr, target->type)) { |
167 | 0 | ha_alert("Proxy '%s': type of fetch not usable with type of stick-table '%s'.\n", |
168 | 0 | curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id); |
169 | 0 | return 0; |
170 | 0 | } |
171 | | |
172 | | /* success */ |
173 | 0 | ha_free(&mrule->table.name); |
174 | 0 | mrule->table.t = target; |
175 | 0 | stktable_alloc_data_type(target, STKTABLE_DT_SERVER_ID, NULL, NULL); |
176 | 0 | stktable_alloc_data_type(target, STKTABLE_DT_SERVER_KEY, NULL, NULL); |
177 | 0 | if (!in_proxies_list(target->proxies_list, curproxy)) { |
178 | 0 | curproxy->next_stkt_ref = target->proxies_list; |
179 | 0 | target->proxies_list = curproxy; |
180 | 0 | } |
181 | 0 | return 1; |
182 | 0 | } |
183 | | |
184 | | void free_stick_rules(struct list *rules) |
185 | 0 | { |
186 | 0 | struct sticking_rule *rule, *ruleb; |
187 | |
|
188 | 0 | list_for_each_entry_safe(rule, ruleb, rules, list) { |
189 | 0 | LIST_DELETE(&rule->list); |
190 | 0 | free_acl_cond(rule->cond); |
191 | 0 | release_sample_expr(rule->expr); |
192 | 0 | free(rule); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | void free_server_rules(struct list *srules) |
197 | 0 | { |
198 | 0 | struct server_rule *srule, *sruleb; |
199 | |
|
200 | 0 | list_for_each_entry_safe(srule, sruleb, srules, list) { |
201 | 0 | LIST_DELETE(&srule->list); |
202 | 0 | free_acl_cond(srule->cond); |
203 | 0 | lf_expr_deinit(&srule->expr); |
204 | 0 | free(srule->file); |
205 | 0 | free(srule); |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | /* Frees proxy members that are common to all proxy types (either regular or |
210 | | * default ones) for a proxy that's about to be destroyed. |
211 | | * This is a subset of the complete proxy or default proxy deinit code. |
212 | | */ |
213 | | static inline void proxy_free_common(struct proxy *px) |
214 | 0 | { |
215 | 0 | struct acl *acl, *aclb; |
216 | 0 | struct logger *log, *logb; |
217 | 0 | struct lf_expr *lf, *lfb; |
218 | 0 | struct eb32_node *node; |
219 | | |
220 | | /* note that the node's key points to p->id */ |
221 | 0 | ebpt_delete(&px->conf.by_name); |
222 | 0 | ha_free(&px->id); |
223 | 0 | LIST_DEL_INIT(&px->global_list); |
224 | 0 | drop_file_name(&px->conf.file); |
225 | 0 | counters_fe_shared_drop(px->fe_counters.shared); |
226 | 0 | counters_be_shared_drop(px->be_counters.shared); |
227 | 0 | ha_free(&px->check_command); |
228 | 0 | ha_free(&px->check_path); |
229 | 0 | ha_free(&px->cookie_name); |
230 | 0 | ha_free(&px->rdp_cookie_name); |
231 | 0 | ha_free(&px->dyncookie_key); |
232 | 0 | ha_free(&px->cookie_domain); |
233 | 0 | ha_free(&px->cookie_attrs); |
234 | 0 | ha_free(&px->lbprm.arg_str); |
235 | 0 | ha_free(&px->capture_name); |
236 | 0 | istfree(&px->monitor_uri); |
237 | 0 | ha_free(&px->conn_src.iface_name); |
238 | 0 | #if defined(CONFIG_HAP_TRANSPARENT) |
239 | 0 | ha_free(&px->conn_src.bind_hdr_name); |
240 | 0 | #endif |
241 | 0 | istfree(&px->server_id_hdr_name); |
242 | 0 | istfree(&px->header_unique_id); |
243 | |
|
244 | 0 | http_ext_clean(px); |
245 | |
|
246 | 0 | list_for_each_entry_safe(acl, aclb, &px->acl, list) { |
247 | 0 | LIST_DELETE(&acl->list); |
248 | 0 | prune_acl(acl); |
249 | 0 | free(acl); |
250 | 0 | } |
251 | |
|
252 | 0 | free_act_rules(&px->tcp_req.inspect_rules); |
253 | 0 | free_act_rules(&px->tcp_rep.inspect_rules); |
254 | 0 | free_act_rules(&px->tcp_req.l4_rules); |
255 | 0 | free_act_rules(&px->tcp_req.l5_rules); |
256 | 0 | free_act_rules(&px->http_req_rules); |
257 | 0 | free_act_rules(&px->http_res_rules); |
258 | 0 | free_act_rules(&px->http_after_res_rules); |
259 | | #ifdef USE_QUIC |
260 | | free_act_rules(&px->quic_init_rules); |
261 | | #endif |
262 | |
|
263 | 0 | lf_expr_deinit(&px->logformat); |
264 | 0 | lf_expr_deinit(&px->logformat_sd); |
265 | 0 | lf_expr_deinit(&px->logformat_error); |
266 | 0 | lf_expr_deinit(&px->format_unique_id); |
267 | |
|
268 | 0 | list_for_each_entry_safe(log, logb, &px->loggers, list) { |
269 | 0 | LIST_DEL_INIT(&log->list); |
270 | 0 | free_logger(log); |
271 | 0 | } |
272 | | |
273 | | /* ensure that remaining lf_expr that were not postchecked (ie: disabled |
274 | | * proxy) don't keep a reference on the proxy which is about to be freed. |
275 | | */ |
276 | 0 | list_for_each_entry_safe(lf, lfb, &px->conf.lf_checks, list) |
277 | 0 | LIST_DEL_INIT(&lf->list); |
278 | |
|
279 | 0 | chunk_destroy(&px->log_tag); |
280 | |
|
281 | 0 | node = eb32_first(&px->conf.log_steps); |
282 | 0 | while (node) { |
283 | 0 | struct eb32_node *prev_node = node; |
284 | | |
285 | | /* log steps directly use the node key as id, they are not encapsulated */ |
286 | 0 | node = eb32_next(node); |
287 | 0 | eb32_delete(prev_node); |
288 | 0 | free(prev_node); |
289 | 0 | } |
290 | |
|
291 | 0 | free_email_alert(px); |
292 | 0 | stats_uri_auth_drop(px->uri_auth); |
293 | 0 | px->uri_auth = NULL; |
294 | 0 | } |
295 | | |
296 | | /* deinit all <p> proxy members, but doesn't touch to the parent pointer |
297 | | * itself |
298 | | */ |
299 | | void deinit_proxy(struct proxy *p) |
300 | 0 | { |
301 | 0 | struct server *s; |
302 | 0 | struct cap_hdr *h,*h_next; |
303 | 0 | struct listener *l,*l_next; |
304 | 0 | struct bind_conf *bind_conf, *bind_back; |
305 | 0 | struct acl_cond *cond, *condb; |
306 | 0 | struct switching_rule *rule, *ruleb; |
307 | 0 | struct redirect_rule *rdr, *rdrb; |
308 | 0 | struct proxy_deinit_fct *pxdf; |
309 | 0 | struct server_deinit_fct *srvdf; |
310 | |
|
311 | 0 | if (!p) |
312 | 0 | return; |
313 | | |
314 | 0 | proxy_free_common(p); |
315 | | |
316 | | /* regular proxy specific cleanup */ |
317 | 0 | release_sample_expr(p->lbprm.expr); |
318 | 0 | free(p->server_state_file_name); |
319 | 0 | free(p->invalid_rep); |
320 | 0 | free(p->invalid_req); |
321 | 0 | if ((p->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_MAP) |
322 | 0 | free(p->lbprm.map.srv); |
323 | |
|
324 | 0 | list_for_each_entry_safe(cond, condb, &p->mon_fail_cond, list) { |
325 | 0 | LIST_DELETE(&cond->list); |
326 | 0 | free_acl_cond(cond); |
327 | 0 | } |
328 | |
|
329 | 0 | guid_remove(&p->guid); |
330 | |
|
331 | 0 | EXTRA_COUNTERS_FREE(p->extra_counters_fe); |
332 | 0 | EXTRA_COUNTERS_FREE(p->extra_counters_be); |
333 | |
|
334 | 0 | free_server_rules(&p->server_rules); |
335 | |
|
336 | 0 | list_for_each_entry_safe(rule, ruleb, &p->switching_rules, list) { |
337 | 0 | LIST_DELETE(&rule->list); |
338 | 0 | free_acl_cond(rule->cond); |
339 | 0 | if (rule->dynamic) |
340 | 0 | lf_expr_deinit(&rule->be.expr); |
341 | 0 | free(rule->file); |
342 | 0 | free(rule); |
343 | 0 | } |
344 | |
|
345 | 0 | list_for_each_entry_safe(rdr, rdrb, &p->redirect_rules, list) { |
346 | 0 | LIST_DELETE(&rdr->list); |
347 | 0 | http_free_redirect_rule(rdr); |
348 | 0 | } |
349 | |
|
350 | 0 | free_stick_rules(&p->storersp_rules); |
351 | 0 | free_stick_rules(&p->sticking_rules); |
352 | |
|
353 | 0 | h = p->req_cap; |
354 | 0 | while (h) { |
355 | 0 | if (p->defpx && h == p->defpx->req_cap) |
356 | 0 | break; |
357 | 0 | h_next = h->next; |
358 | 0 | free(h->name); |
359 | 0 | pool_destroy(h->pool); |
360 | 0 | free(h); |
361 | 0 | h = h_next; |
362 | 0 | }/* end while(h) */ |
363 | |
|
364 | 0 | h = p->rsp_cap; |
365 | 0 | while (h) { |
366 | 0 | if (p->defpx && h == p->defpx->rsp_cap) |
367 | 0 | break; |
368 | 0 | h_next = h->next; |
369 | 0 | free(h->name); |
370 | 0 | pool_destroy(h->pool); |
371 | 0 | free(h); |
372 | 0 | h = h_next; |
373 | 0 | }/* end while(h) */ |
374 | |
|
375 | 0 | s = p->srv; |
376 | 0 | while (s) { |
377 | 0 | list_for_each_entry(srvdf, &server_deinit_list, list) |
378 | 0 | srvdf->fct(s); |
379 | |
|
380 | 0 | if (p->lbprm.server_deinit) |
381 | 0 | p->lbprm.server_deinit(s); |
382 | |
|
383 | 0 | s = srv_drop(s); |
384 | 0 | }/* end while(s) */ |
385 | | |
386 | | /* also free default-server parameters since some of them might have |
387 | | * been dynamically allocated (e.g.: config hints, cookies, ssl..) |
388 | | */ |
389 | 0 | srv_free_params(&p->defsrv); |
390 | |
|
391 | 0 | if (p->lbprm.proxy_deinit) |
392 | 0 | p->lbprm.proxy_deinit(p); |
393 | |
|
394 | 0 | list_for_each_entry_safe(l, l_next, &p->conf.listeners, by_fe) { |
395 | 0 | guid_remove(&l->guid); |
396 | 0 | LIST_DELETE(&l->by_fe); |
397 | 0 | LIST_DELETE(&l->by_bind); |
398 | 0 | free(l->name); |
399 | 0 | free(l->label); |
400 | 0 | free(l->per_thr); |
401 | 0 | if (l->counters) { |
402 | 0 | counters_fe_shared_drop(l->counters->shared); |
403 | 0 | free(l->counters); |
404 | 0 | } |
405 | 0 | task_destroy(l->rx.rhttp.task); |
406 | |
|
407 | 0 | EXTRA_COUNTERS_FREE(l->extra_counters); |
408 | 0 | free(l); |
409 | 0 | } |
410 | | |
411 | | /* Release unused SSL configs. */ |
412 | 0 | list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) { |
413 | 0 | if (bind_conf->xprt->destroy_bind_conf) |
414 | 0 | bind_conf->xprt->destroy_bind_conf(bind_conf); |
415 | 0 | free(bind_conf->file); |
416 | 0 | free(bind_conf->arg); |
417 | 0 | free(bind_conf->settings.interface); |
418 | 0 | LIST_DELETE(&bind_conf->by_fe); |
419 | 0 | free(bind_conf->guid_prefix); |
420 | 0 | free(bind_conf->rhttp_srvname); |
421 | | #ifdef USE_QUIC |
422 | | free(bind_conf->quic_cc_algo); |
423 | | #endif |
424 | 0 | free(bind_conf); |
425 | 0 | } |
426 | |
|
427 | 0 | flt_deinit(p); |
428 | |
|
429 | 0 | list_for_each_entry(pxdf, &proxy_deinit_list, list) |
430 | 0 | pxdf->fct(p); |
431 | |
|
432 | 0 | free(p->desc); |
433 | |
|
434 | 0 | task_destroy(p->task); |
435 | |
|
436 | 0 | pool_destroy(p->req_cap_pool); |
437 | 0 | pool_destroy(p->rsp_cap_pool); |
438 | |
|
439 | 0 | stktable_deinit(p->table); |
440 | 0 | ha_free(&p->table); |
441 | 0 | ha_free(&p->per_tgrp); |
442 | |
|
443 | 0 | HA_RWLOCK_DESTROY(&p->lbprm.lock); |
444 | 0 | HA_RWLOCK_DESTROY(&p->lock); |
445 | |
|
446 | 0 | proxy_unref_defaults(p); |
447 | 0 | } |
448 | | |
449 | | /* deinit and free <p> proxy */ |
450 | | void free_proxy(struct proxy *p) |
451 | 0 | { |
452 | 0 | deinit_proxy(p); |
453 | 0 | ha_free(&p); |
454 | 0 | } |
455 | | |
456 | | /* |
457 | | * This function returns a string containing a name describing capabilities to |
458 | | * report comprehensible error messages. Specifically, it will return the words |
459 | | * "frontend", "backend" when appropriate, "defaults" if it corresponds to a |
460 | | * defaults section, or "proxy" for all other cases including the proxies |
461 | | * declared in "listen" mode. |
462 | | */ |
463 | | const char *proxy_cap_str(int cap) |
464 | 0 | { |
465 | 0 | if (cap & PR_CAP_DEF) |
466 | 0 | return "defaults"; |
467 | | |
468 | 0 | if ((cap & PR_CAP_LISTEN) != PR_CAP_LISTEN) { |
469 | 0 | if (cap & PR_CAP_FE) |
470 | 0 | return "frontend"; |
471 | 0 | else if (cap & PR_CAP_BE) |
472 | 0 | return "backend"; |
473 | 0 | } |
474 | 0 | return "proxy"; |
475 | 0 | } |
476 | | |
477 | | /* |
478 | | * This function returns a string containing the mode of the proxy in a format |
479 | | * suitable for error messages. |
480 | | */ |
481 | 0 | const char *proxy_mode_str(int mode) { |
482 | |
|
483 | 0 | if (mode == PR_MODE_TCP) |
484 | 0 | return "tcp"; |
485 | 0 | else if (mode == PR_MODE_HTTP) |
486 | 0 | return "http"; |
487 | 0 | else if (mode == PR_MODE_CLI) |
488 | 0 | return "cli"; |
489 | 0 | else if (mode == PR_MODE_SYSLOG) |
490 | 0 | return "syslog"; |
491 | 0 | else if (mode == PR_MODE_PEERS) |
492 | 0 | return "peers"; |
493 | 0 | else if (mode == PR_MODE_SPOP) |
494 | 0 | return "spop"; |
495 | 0 | else |
496 | 0 | return "unknown"; |
497 | 0 | } |
498 | | |
499 | | /* try to find among known options the one that looks closest to <word> by |
500 | | * counting transitions between letters, digits and other characters. Will |
501 | | * return the best matching word if found, otherwise NULL. An optional array |
502 | | * of extra words to compare may be passed in <extra>, but it must then be |
503 | | * terminated by a NULL entry. If unused it may be NULL. |
504 | | */ |
505 | | const char *proxy_find_best_option(const char *word, const char **extra) |
506 | 0 | { |
507 | 0 | uint8_t word_sig[1024]; |
508 | 0 | uint8_t list_sig[1024]; |
509 | 0 | const char *best_ptr = NULL; |
510 | 0 | int dist, best_dist = INT_MAX; |
511 | 0 | int index; |
512 | |
|
513 | 0 | make_word_fingerprint(word_sig, word); |
514 | |
|
515 | 0 | for (index = 0; cfg_opts[index].name; index++) { |
516 | 0 | make_word_fingerprint(list_sig, cfg_opts[index].name); |
517 | 0 | dist = word_fingerprint_distance(word_sig, list_sig); |
518 | 0 | if (dist < best_dist) { |
519 | 0 | best_dist = dist; |
520 | 0 | best_ptr = cfg_opts[index].name; |
521 | 0 | } |
522 | 0 | } |
523 | |
|
524 | 0 | for (index = 0; cfg_opts2[index].name; index++) { |
525 | 0 | make_word_fingerprint(list_sig, cfg_opts2[index].name); |
526 | 0 | dist = word_fingerprint_distance(word_sig, list_sig); |
527 | 0 | if (dist < best_dist) { |
528 | 0 | best_dist = dist; |
529 | 0 | best_ptr = cfg_opts2[index].name; |
530 | 0 | } |
531 | 0 | } |
532 | |
|
533 | 0 | while (extra && *extra) { |
534 | 0 | make_word_fingerprint(list_sig, *extra); |
535 | 0 | dist = word_fingerprint_distance(word_sig, list_sig); |
536 | 0 | if (dist < best_dist) { |
537 | 0 | best_dist = dist; |
538 | 0 | best_ptr = *extra; |
539 | 0 | } |
540 | 0 | extra++; |
541 | 0 | } |
542 | |
|
543 | 0 | if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr))) |
544 | 0 | best_ptr = NULL; |
545 | 0 | return best_ptr; |
546 | 0 | } |
547 | | |
548 | | /* This function parses a "timeout" statement in a proxy section. It returns |
549 | | * -1 if there is any error, 1 for a warning, otherwise zero. If it does not |
550 | | * return zero, it will write an error or warning message into a preallocated |
551 | | * buffer returned at <err>. The trailing is not be written. The function must |
552 | | * be called with <args> pointing to the first command line word, with <proxy> |
553 | | * pointing to the proxy being parsed, and <defpx> to the default proxy or NULL. |
554 | | * As a special case for compatibility with older configs, it also accepts |
555 | | * "{cli|srv|con}timeout" in args[0]. |
556 | | */ |
557 | | static int proxy_parse_timeout(char **args, int section, struct proxy *proxy, |
558 | | const struct proxy *defpx, const char *file, int line, |
559 | | char **err) |
560 | 0 | { |
561 | 0 | unsigned timeout; |
562 | 0 | int retval, cap; |
563 | 0 | const char *res, *name; |
564 | 0 | int *tv = NULL; |
565 | 0 | const int *td = NULL; |
566 | |
|
567 | 0 | retval = 0; |
568 | | |
569 | | /* simply skip "timeout" but remain compatible with old form */ |
570 | 0 | if (strcmp(args[0], "timeout") == 0) |
571 | 0 | args++; |
572 | |
|
573 | 0 | name = args[0]; |
574 | 0 | if (strcmp(args[0], "client") == 0) { |
575 | 0 | name = "client"; |
576 | 0 | tv = &proxy->timeout.client; |
577 | 0 | td = &defpx->timeout.client; |
578 | 0 | cap = PR_CAP_FE; |
579 | 0 | } else if (strcmp(args[0], "tarpit") == 0) { |
580 | 0 | tv = &proxy->timeout.tarpit; |
581 | 0 | td = &defpx->timeout.tarpit; |
582 | 0 | cap = PR_CAP_FE | PR_CAP_BE; |
583 | 0 | } else if (strcmp(args[0], "client-hs") == 0) { |
584 | 0 | tv = &proxy->timeout.client_hs; |
585 | 0 | td = &defpx->timeout.client_hs; |
586 | 0 | cap = PR_CAP_FE; |
587 | 0 | } else if (strcmp(args[0], "http-keep-alive") == 0) { |
588 | 0 | tv = &proxy->timeout.httpka; |
589 | 0 | td = &defpx->timeout.httpka; |
590 | 0 | cap = PR_CAP_FE | PR_CAP_BE; |
591 | 0 | } else if (strcmp(args[0], "http-request") == 0) { |
592 | 0 | tv = &proxy->timeout.httpreq; |
593 | 0 | td = &defpx->timeout.httpreq; |
594 | 0 | cap = PR_CAP_FE | PR_CAP_BE; |
595 | 0 | } else if (strcmp(args[0], "server") == 0) { |
596 | 0 | name = "server"; |
597 | 0 | tv = &proxy->timeout.server; |
598 | 0 | td = &defpx->timeout.server; |
599 | 0 | cap = PR_CAP_BE; |
600 | 0 | } else if (strcmp(args[0], "connect") == 0) { |
601 | 0 | name = "connect"; |
602 | 0 | tv = &proxy->timeout.connect; |
603 | 0 | td = &defpx->timeout.connect; |
604 | 0 | cap = PR_CAP_BE; |
605 | 0 | } else if (strcmp(args[0], "check") == 0) { |
606 | 0 | tv = &proxy->timeout.check; |
607 | 0 | td = &defpx->timeout.check; |
608 | 0 | cap = PR_CAP_BE; |
609 | 0 | } else if (strcmp(args[0], "queue") == 0) { |
610 | 0 | tv = &proxy->timeout.queue; |
611 | 0 | td = &defpx->timeout.queue; |
612 | 0 | cap = PR_CAP_BE; |
613 | 0 | } else if (strcmp(args[0], "tunnel") == 0) { |
614 | 0 | tv = &proxy->timeout.tunnel; |
615 | 0 | td = &defpx->timeout.tunnel; |
616 | 0 | cap = PR_CAP_BE; |
617 | 0 | } else if (strcmp(args[0], "client-fin") == 0) { |
618 | 0 | tv = &proxy->timeout.clientfin; |
619 | 0 | td = &defpx->timeout.clientfin; |
620 | 0 | cap = PR_CAP_FE; |
621 | 0 | } else if (strcmp(args[0], "server-fin") == 0) { |
622 | 0 | tv = &proxy->timeout.serverfin; |
623 | 0 | td = &defpx->timeout.serverfin; |
624 | 0 | cap = PR_CAP_BE; |
625 | 0 | } else if (strcmp(args[0], "clitimeout") == 0) { |
626 | 0 | memprintf(err, "the '%s' directive is not supported anymore since HAProxy 2.1. Use 'timeout client'.", args[0]); |
627 | 0 | return -1; |
628 | 0 | } else if (strcmp(args[0], "srvtimeout") == 0) { |
629 | 0 | memprintf(err, "the '%s' directive is not supported anymore since HAProxy 2.1. Use 'timeout server'.", args[0]); |
630 | 0 | return -1; |
631 | 0 | } else if (strcmp(args[0], "contimeout") == 0) { |
632 | 0 | memprintf(err, "the '%s' directive is not supported anymore since HAProxy 2.1. Use 'timeout connect'.", args[0]); |
633 | 0 | return -1; |
634 | 0 | } else { |
635 | 0 | memprintf(err, |
636 | 0 | "'timeout' supports 'client', 'server', 'connect', 'check', " |
637 | 0 | "'queue', 'handshake', 'http-keep-alive', 'http-request', 'tunnel', 'tarpit', " |
638 | 0 | "'client-fin' and 'server-fin' (got '%s')", |
639 | 0 | args[0]); |
640 | 0 | return -1; |
641 | 0 | } |
642 | | |
643 | 0 | if (*args[1] == 0) { |
644 | 0 | memprintf(err, "'timeout %s' expects an integer value (in milliseconds)", name); |
645 | 0 | return -1; |
646 | 0 | } |
647 | | |
648 | 0 | res = parse_time_err(args[1], &timeout, TIME_UNIT_MS); |
649 | 0 | if (res == PARSE_TIME_OVER) { |
650 | 0 | memprintf(err, "timer overflow in argument '%s' to 'timeout %s' (maximum value is 2147483647 ms or ~24.8 days)", |
651 | 0 | args[1], name); |
652 | 0 | return -1; |
653 | 0 | } |
654 | 0 | else if (res == PARSE_TIME_UNDER) { |
655 | 0 | memprintf(err, "timer underflow in argument '%s' to 'timeout %s' (minimum non-null value is 1 ms)", |
656 | 0 | args[1], name); |
657 | 0 | return -1; |
658 | 0 | } |
659 | 0 | else if (res) { |
660 | 0 | memprintf(err, "unexpected character '%c' in 'timeout %s'", *res, name); |
661 | 0 | return -1; |
662 | 0 | } |
663 | | |
664 | 0 | if (warn_if_lower(args[1], 100)) { |
665 | 0 | memprintf(err, "'timeout %s %u' in %s '%s' is suspiciously small for a value in milliseconds. Please use an explicit unit ('%ums') if that was the intent.", |
666 | 0 | name, timeout, proxy_type_str(proxy), proxy->id, timeout); |
667 | 0 | retval = 1; |
668 | 0 | } |
669 | |
|
670 | 0 | if (!(proxy->cap & cap)) { |
671 | 0 | memprintf(err, "'timeout %s' will be ignored because %s '%s' has no %s capability", |
672 | 0 | name, proxy_type_str(proxy), proxy->id, |
673 | 0 | (cap & PR_CAP_BE) ? "backend" : "frontend"); |
674 | 0 | retval = 1; |
675 | 0 | } |
676 | 0 | else if (defpx && *tv != *td) { |
677 | 0 | memprintf(err, "overwriting 'timeout %s' which was already specified", name); |
678 | 0 | retval = 1; |
679 | 0 | } |
680 | |
|
681 | 0 | if (*args[2] != 0) { |
682 | 0 | memprintf(err, "'timeout %s' : unexpected extra argument '%s' after value '%s'.", name, args[2], args[1]); |
683 | 0 | retval = -1; |
684 | 0 | } |
685 | |
|
686 | 0 | *tv = MS_TO_TICKS(timeout); |
687 | 0 | return retval; |
688 | 0 | } |
689 | | |
690 | | /* This function parses a "rate-limit" statement in a proxy section. It returns |
691 | | * -1 if there is any error, 1 for a warning, otherwise zero. If it does not |
692 | | * return zero, it will write an error or warning message into a preallocated |
693 | | * buffer returned at <err>. The function must be called with <args> pointing |
694 | | * to the first command line word, with <proxy> pointing to the proxy being |
695 | | * parsed, and <defpx> to the default proxy or NULL. |
696 | | */ |
697 | | static int proxy_parse_rate_limit(char **args, int section, struct proxy *proxy, |
698 | | const struct proxy *defpx, const char *file, int line, |
699 | | char **err) |
700 | 0 | { |
701 | 0 | int retval; |
702 | 0 | char *res; |
703 | 0 | unsigned int *tv = NULL; |
704 | 0 | const unsigned int *td = NULL; |
705 | 0 | unsigned int val; |
706 | |
|
707 | 0 | retval = 0; |
708 | |
|
709 | 0 | if (strcmp(args[1], "sessions") == 0) { |
710 | 0 | tv = &proxy->fe_sps_lim; |
711 | 0 | td = &defpx->fe_sps_lim; |
712 | 0 | } |
713 | 0 | else { |
714 | 0 | memprintf(err, "'%s' only supports 'sessions' (got '%s')", args[0], args[1]); |
715 | 0 | return -1; |
716 | 0 | } |
717 | | |
718 | 0 | if (*args[2] == 0) { |
719 | 0 | memprintf(err, "'%s %s' expects expects an integer value (in sessions/second)", args[0], args[1]); |
720 | 0 | return -1; |
721 | 0 | } |
722 | | |
723 | 0 | val = strtoul(args[2], &res, 0); |
724 | 0 | if (*res) { |
725 | 0 | memprintf(err, "'%s %s' : unexpected character '%c' in integer value '%s'", args[0], args[1], *res, args[2]); |
726 | 0 | return -1; |
727 | 0 | } |
728 | | |
729 | 0 | if (!(proxy->cap & PR_CAP_FE)) { |
730 | 0 | memprintf(err, "%s %s will be ignored because %s '%s' has no frontend capability", |
731 | 0 | args[0], args[1], proxy_type_str(proxy), proxy->id); |
732 | 0 | retval = 1; |
733 | 0 | } |
734 | 0 | else if (defpx && *tv != *td) { |
735 | 0 | memprintf(err, "overwriting %s %s which was already specified", args[0], args[1]); |
736 | 0 | retval = 1; |
737 | 0 | } |
738 | |
|
739 | 0 | *tv = val; |
740 | 0 | return retval; |
741 | 0 | } |
742 | | |
743 | | /* This function parses a "max-keep-alive-queue" statement in a proxy section. |
744 | | * It returns -1 if there is any error, 1 for a warning, otherwise zero. If it |
745 | | * does not return zero, it will write an error or warning message into a |
746 | | * preallocated buffer returned at <err>. The function must be called with |
747 | | * <args> pointing to the first command line word, with <proxy> pointing to |
748 | | * the proxy being parsed, and <defpx> to the default proxy or NULL. |
749 | | */ |
750 | | static int proxy_parse_max_ka_queue(char **args, int section, struct proxy *proxy, |
751 | | const struct proxy *defpx, const char *file, int line, |
752 | | char **err) |
753 | 0 | { |
754 | 0 | int retval; |
755 | 0 | char *res; |
756 | 0 | unsigned int val; |
757 | |
|
758 | 0 | retval = 0; |
759 | |
|
760 | 0 | if (*args[1] == 0) { |
761 | 0 | memprintf(err, "'%s' expects expects an integer value (or -1 to disable)", args[0]); |
762 | 0 | return -1; |
763 | 0 | } |
764 | | |
765 | 0 | val = strtol(args[1], &res, 0); |
766 | 0 | if (*res) { |
767 | 0 | memprintf(err, "'%s' : unexpected character '%c' in integer value '%s'", args[0], *res, args[1]); |
768 | 0 | return -1; |
769 | 0 | } |
770 | | |
771 | 0 | if (!(proxy->cap & PR_CAP_BE)) { |
772 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no backend capability", |
773 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
774 | 0 | retval = 1; |
775 | 0 | } |
776 | | |
777 | | /* we store <val+1> so that a user-facing value of -1 is stored as zero (default) */ |
778 | 0 | proxy->max_ka_queue = val + 1; |
779 | 0 | return retval; |
780 | 0 | } |
781 | | |
782 | | /* This function parses a "declare" statement in a proxy section. It returns -1 |
783 | | * if there is any error, 1 for warning, otherwise 0. If it does not return zero, |
784 | | * it will write an error or warning message into a preallocated buffer returned |
785 | | * at <err>. The function must be called with <args> pointing to the first command |
786 | | * line word, with <proxy> pointing to the proxy being parsed, and <defpx> to the |
787 | | * default proxy or NULL. |
788 | | */ |
789 | | static int proxy_parse_declare(char **args, int section, struct proxy *curpx, |
790 | | const struct proxy *defpx, const char *file, int line, |
791 | | char **err) |
792 | 0 | { |
793 | | /* Capture keyword wannot be declared in a default proxy. */ |
794 | 0 | if (curpx == defpx) { |
795 | 0 | memprintf(err, "'%s' not available in default section", args[0]); |
796 | 0 | return -1; |
797 | 0 | } |
798 | | |
799 | | /* Capture keyword is only available in frontend. */ |
800 | 0 | if (!(curpx->cap & PR_CAP_FE)) { |
801 | 0 | memprintf(err, "'%s' only available in frontend or listen section", args[0]); |
802 | 0 | return -1; |
803 | 0 | } |
804 | | |
805 | | /* Check mandatory second keyword. */ |
806 | 0 | if (!args[1] || !*args[1]) { |
807 | 0 | memprintf(err, "'%s' needs a second keyword that specify the type of declaration ('capture')", args[0]); |
808 | 0 | return -1; |
809 | 0 | } |
810 | | |
811 | | /* Actually, declare is only available for declaring capture |
812 | | * slot, but in the future it can declare maps or variables. |
813 | | * So, this section permits to check and switch according with |
814 | | * the second keyword. |
815 | | */ |
816 | 0 | if (strcmp(args[1], "capture") == 0) { |
817 | 0 | char *error = NULL; |
818 | 0 | long len; |
819 | 0 | struct cap_hdr *hdr; |
820 | | |
821 | | /* Check the next keyword. */ |
822 | 0 | if (!args[2] || !*args[2] || |
823 | 0 | (strcmp(args[2], "response") != 0 && |
824 | 0 | strcmp(args[2], "request") != 0)) { |
825 | 0 | memprintf(err, "'%s %s' requires a direction ('request' or 'response')", args[0], args[1]); |
826 | 0 | return -1; |
827 | 0 | } |
828 | | |
829 | | /* Check the 'len' keyword. */ |
830 | 0 | if (!args[3] || !*args[3] || strcmp(args[3], "len") != 0) { |
831 | 0 | memprintf(err, "'%s %s' requires a capture length ('len')", args[0], args[1]); |
832 | 0 | return -1; |
833 | 0 | } |
834 | | |
835 | | /* Check the length value. */ |
836 | 0 | if (!args[4] || !*args[4]) { |
837 | 0 | memprintf(err, "'%s %s': 'len' requires a numeric value that represents the " |
838 | 0 | "capture length", |
839 | 0 | args[0], args[1]); |
840 | 0 | return -1; |
841 | 0 | } |
842 | | |
843 | | /* convert the length value. */ |
844 | 0 | len = strtol(args[4], &error, 10); |
845 | 0 | if (*error != '\0') { |
846 | 0 | memprintf(err, "'%s %s': cannot parse the length '%s'.", |
847 | 0 | args[0], args[1], args[3]); |
848 | 0 | return -1; |
849 | 0 | } |
850 | | |
851 | | /* check length. */ |
852 | 0 | if (len <= 0) { |
853 | 0 | memprintf(err, "length must be > 0"); |
854 | 0 | return -1; |
855 | 0 | } |
856 | | |
857 | | /* register the capture. */ |
858 | 0 | hdr = calloc(1, sizeof(*hdr)); |
859 | 0 | if (!hdr) { |
860 | 0 | memprintf(err, "proxy '%s': out of memory while registering a capture", curpx->id); |
861 | 0 | return -1; |
862 | 0 | } |
863 | 0 | hdr->name = NULL; /* not a header capture */ |
864 | 0 | hdr->namelen = 0; |
865 | 0 | hdr->len = len; |
866 | 0 | hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED); |
867 | |
|
868 | 0 | if (strcmp(args[2], "request") == 0) { |
869 | 0 | hdr->next = curpx->req_cap; |
870 | 0 | hdr->index = curpx->nb_req_cap++; |
871 | 0 | curpx->req_cap = hdr; |
872 | 0 | } |
873 | 0 | if (strcmp(args[2], "response") == 0) { |
874 | 0 | hdr->next = curpx->rsp_cap; |
875 | 0 | hdr->index = curpx->nb_rsp_cap++; |
876 | 0 | curpx->rsp_cap = hdr; |
877 | 0 | } |
878 | 0 | return 0; |
879 | 0 | } |
880 | 0 | else { |
881 | 0 | memprintf(err, "unknown declaration type '%s' (supports 'capture')", args[1]); |
882 | 0 | return -1; |
883 | 0 | } |
884 | 0 | } |
885 | | |
886 | | /* This function parses a "retry-on" statement */ |
887 | | static int |
888 | | proxy_parse_retry_on(char **args, int section, struct proxy *curpx, |
889 | | const struct proxy *defpx, const char *file, int line, |
890 | | char **err) |
891 | 0 | { |
892 | 0 | int i; |
893 | |
|
894 | 0 | if (!(*args[1])) { |
895 | 0 | memprintf(err, "'%s' needs at least one keyword to specify when to retry", args[0]); |
896 | 0 | return -1; |
897 | 0 | } |
898 | 0 | if (!(curpx->cap & PR_CAP_BE)) { |
899 | 0 | memprintf(err, "'%s' only available in backend or listen section", args[0]); |
900 | 0 | return -1; |
901 | 0 | } |
902 | 0 | curpx->retry_type = 0; |
903 | 0 | for (i = 1; *(args[i]); i++) { |
904 | 0 | if (strcmp(args[i], "conn-failure") == 0) |
905 | 0 | curpx->retry_type |= PR_RE_CONN_FAILED; |
906 | 0 | else if (strcmp(args[i], "empty-response") == 0) |
907 | 0 | curpx->retry_type |= PR_RE_DISCONNECTED; |
908 | 0 | else if (strcmp(args[i], "response-timeout") == 0) |
909 | 0 | curpx->retry_type |= PR_RE_TIMEOUT; |
910 | 0 | else if (strcmp(args[i], "401") == 0) |
911 | 0 | curpx->retry_type |= PR_RE_401; |
912 | 0 | else if (strcmp(args[i], "403") == 0) |
913 | 0 | curpx->retry_type |= PR_RE_403; |
914 | 0 | else if (strcmp(args[i], "404") == 0) |
915 | 0 | curpx->retry_type |= PR_RE_404; |
916 | 0 | else if (strcmp(args[i], "408") == 0) |
917 | 0 | curpx->retry_type |= PR_RE_408; |
918 | 0 | else if (strcmp(args[i], "421") == 0) |
919 | 0 | curpx->retry_type |= PR_RE_421; |
920 | 0 | else if (strcmp(args[i], "425") == 0) |
921 | 0 | curpx->retry_type |= PR_RE_425; |
922 | 0 | else if (strcmp(args[i], "429") == 0) |
923 | 0 | curpx->retry_type |= PR_RE_429; |
924 | 0 | else if (strcmp(args[i], "500") == 0) |
925 | 0 | curpx->retry_type |= PR_RE_500; |
926 | 0 | else if (strcmp(args[i], "501") == 0) |
927 | 0 | curpx->retry_type |= PR_RE_501; |
928 | 0 | else if (strcmp(args[i], "502") == 0) |
929 | 0 | curpx->retry_type |= PR_RE_502; |
930 | 0 | else if (strcmp(args[i], "503") == 0) |
931 | 0 | curpx->retry_type |= PR_RE_503; |
932 | 0 | else if (strcmp(args[i], "504") == 0) |
933 | 0 | curpx->retry_type |= PR_RE_504; |
934 | 0 | else if (strcmp(args[i], "0rtt-rejected") == 0) |
935 | 0 | curpx->retry_type |= PR_RE_EARLY_ERROR; |
936 | 0 | else if (strcmp(args[i], "junk-response") == 0) |
937 | 0 | curpx->retry_type |= PR_RE_JUNK_REQUEST; |
938 | 0 | else if (!(strcmp(args[i], "all-retryable-errors"))) |
939 | 0 | curpx->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | |
940 | 0 | PR_RE_TIMEOUT | PR_RE_500 | PR_RE_502 | |
941 | 0 | PR_RE_503 | PR_RE_504 | PR_RE_EARLY_ERROR | |
942 | 0 | PR_RE_JUNK_REQUEST; |
943 | 0 | else if (strcmp(args[i], "none") == 0) { |
944 | 0 | if (i != 1 || *args[i + 1]) { |
945 | 0 | memprintf(err, "'%s' 'none' keyworld only usable alone", args[0]); |
946 | 0 | return -1; |
947 | 0 | } |
948 | 0 | } else { |
949 | 0 | memprintf(err, "'%s': unknown keyword '%s'", args[0], args[i]); |
950 | 0 | return -1; |
951 | 0 | } |
952 | |
|
953 | 0 | } |
954 | | |
955 | | |
956 | 0 | return 0; |
957 | 0 | } |
958 | | |
959 | | /* This function parses a "hash-preserve-affinity" statement */ |
960 | | static int |
961 | | proxy_parse_hash_preserve_affinity(char **args, int section, struct proxy *curpx, |
962 | | const struct proxy *defpx, const char *file, int line, |
963 | | char **err) |
964 | 0 | { |
965 | 0 | if (!(*args[1])) { |
966 | 0 | memprintf(err, "'%s' needs a keyword to specify when to preserve hash affinity", args[0]); |
967 | 0 | return -1; |
968 | 0 | } |
969 | 0 | if (!(curpx->cap & PR_CAP_BE)) { |
970 | 0 | memprintf(err, "'%s' only available in backend or listen section", args[0]); |
971 | 0 | return -1; |
972 | 0 | } |
973 | | |
974 | 0 | curpx->options3 &= ~PR_O3_HASHAFNTY_MASK; |
975 | |
|
976 | 0 | if (strcmp(args[1], "always") == 0) |
977 | 0 | curpx->options3 |= PR_O3_HASHAFNTY_ALWS; |
978 | 0 | else if (strcmp(args[1], "maxconn") == 0) |
979 | 0 | curpx->options3 |= PR_O3_HASHAFNTY_MAXCONN; |
980 | 0 | else if (strcmp(args[1], "maxqueue") == 0) |
981 | 0 | curpx->options3 |= PR_O3_HASHAFNTY_MAXQUEUE; |
982 | 0 | else { |
983 | 0 | memprintf(err, "'%s': unknown keyword '%s'", args[0], args[1]); |
984 | 0 | return -1; |
985 | 0 | } |
986 | | |
987 | 0 | return 0; |
988 | 0 | } |
989 | | |
990 | | #ifdef TCP_KEEPCNT |
991 | | /* This function parses "{cli|srv}tcpka-cnt" statements */ |
992 | | static int proxy_parse_tcpka_cnt(char **args, int section, struct proxy *proxy, |
993 | | const struct proxy *defpx, const char *file, int line, |
994 | | char **err) |
995 | 0 | { |
996 | 0 | int retval; |
997 | 0 | char *res; |
998 | 0 | unsigned int tcpka_cnt; |
999 | |
|
1000 | 0 | retval = 0; |
1001 | |
|
1002 | 0 | if (*args[1] == 0) { |
1003 | 0 | memprintf(err, "'%s' expects an integer value", args[0]); |
1004 | 0 | return -1; |
1005 | 0 | } |
1006 | | |
1007 | 0 | tcpka_cnt = strtol(args[1], &res, 0); |
1008 | 0 | if (*res) { |
1009 | 0 | memprintf(err, "'%s' : unexpected character '%c' in integer value '%s'", args[0], *res, args[1]); |
1010 | 0 | return -1; |
1011 | 0 | } |
1012 | | |
1013 | 0 | if (strcmp(args[0], "clitcpka-cnt") == 0) { |
1014 | 0 | if (!(proxy->cap & PR_CAP_FE)) { |
1015 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no frontend capability", |
1016 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
1017 | 0 | retval = 1; |
1018 | 0 | } |
1019 | 0 | proxy->clitcpka_cnt = tcpka_cnt; |
1020 | 0 | } else if (strcmp(args[0], "srvtcpka-cnt") == 0) { |
1021 | 0 | if (!(proxy->cap & PR_CAP_BE)) { |
1022 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no backend capability", |
1023 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
1024 | 0 | retval = 1; |
1025 | 0 | } |
1026 | 0 | proxy->srvtcpka_cnt = tcpka_cnt; |
1027 | 0 | } else { |
1028 | | /* unreachable */ |
1029 | 0 | memprintf(err, "'%s': unknown keyword", args[0]); |
1030 | 0 | return -1; |
1031 | 0 | } |
1032 | | |
1033 | 0 | return retval; |
1034 | 0 | } |
1035 | | #endif |
1036 | | |
1037 | | #ifdef TCP_KEEPIDLE |
1038 | | /* This function parses "{cli|srv}tcpka-idle" statements */ |
1039 | | static int proxy_parse_tcpka_idle(char **args, int section, struct proxy *proxy, |
1040 | | const struct proxy *defpx, const char *file, int line, |
1041 | | char **err) |
1042 | 0 | { |
1043 | 0 | int retval; |
1044 | 0 | const char *res; |
1045 | 0 | unsigned int tcpka_idle; |
1046 | |
|
1047 | 0 | retval = 0; |
1048 | |
|
1049 | 0 | if (*args[1] == 0) { |
1050 | 0 | memprintf(err, "'%s' expects an integer value", args[0]); |
1051 | 0 | return -1; |
1052 | 0 | } |
1053 | 0 | res = parse_time_err(args[1], &tcpka_idle, TIME_UNIT_S); |
1054 | 0 | if (res == PARSE_TIME_OVER) { |
1055 | 0 | memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)", |
1056 | 0 | args[1], args[0]); |
1057 | 0 | return -1; |
1058 | 0 | } |
1059 | 0 | else if (res == PARSE_TIME_UNDER) { |
1060 | 0 | memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)", |
1061 | 0 | args[1], args[0]); |
1062 | 0 | return -1; |
1063 | 0 | } |
1064 | 0 | else if (res) { |
1065 | 0 | memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]); |
1066 | 0 | return -1; |
1067 | 0 | } |
1068 | | |
1069 | 0 | if (strcmp(args[0], "clitcpka-idle") == 0) { |
1070 | 0 | if (!(proxy->cap & PR_CAP_FE)) { |
1071 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no frontend capability", |
1072 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
1073 | 0 | retval = 1; |
1074 | 0 | } |
1075 | 0 | proxy->clitcpka_idle = tcpka_idle; |
1076 | 0 | } else if (strcmp(args[0], "srvtcpka-idle") == 0) { |
1077 | 0 | if (!(proxy->cap & PR_CAP_BE)) { |
1078 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no backend capability", |
1079 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
1080 | 0 | retval = 1; |
1081 | 0 | } |
1082 | 0 | proxy->srvtcpka_idle = tcpka_idle; |
1083 | 0 | } else { |
1084 | | /* unreachable */ |
1085 | 0 | memprintf(err, "'%s': unknown keyword", args[0]); |
1086 | 0 | return -1; |
1087 | 0 | } |
1088 | | |
1089 | 0 | return retval; |
1090 | 0 | } |
1091 | | #endif |
1092 | | |
1093 | | #ifdef TCP_KEEPINTVL |
1094 | | /* This function parses "{cli|srv}tcpka-intvl" statements */ |
1095 | | static int proxy_parse_tcpka_intvl(char **args, int section, struct proxy *proxy, |
1096 | | const struct proxy *defpx, const char *file, int line, |
1097 | | char **err) |
1098 | 0 | { |
1099 | 0 | int retval; |
1100 | 0 | const char *res; |
1101 | 0 | unsigned int tcpka_intvl; |
1102 | |
|
1103 | 0 | retval = 0; |
1104 | |
|
1105 | 0 | if (*args[1] == 0) { |
1106 | 0 | memprintf(err, "'%s' expects an integer value", args[0]); |
1107 | 0 | return -1; |
1108 | 0 | } |
1109 | 0 | res = parse_time_err(args[1], &tcpka_intvl, TIME_UNIT_S); |
1110 | 0 | if (res == PARSE_TIME_OVER) { |
1111 | 0 | memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)", |
1112 | 0 | args[1], args[0]); |
1113 | 0 | return -1; |
1114 | 0 | } |
1115 | 0 | else if (res == PARSE_TIME_UNDER) { |
1116 | 0 | memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)", |
1117 | 0 | args[1], args[0]); |
1118 | 0 | return -1; |
1119 | 0 | } |
1120 | 0 | else if (res) { |
1121 | 0 | memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]); |
1122 | 0 | return -1; |
1123 | 0 | } |
1124 | | |
1125 | 0 | if (strcmp(args[0], "clitcpka-intvl") == 0) { |
1126 | 0 | if (!(proxy->cap & PR_CAP_FE)) { |
1127 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no frontend capability", |
1128 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
1129 | 0 | retval = 1; |
1130 | 0 | } |
1131 | 0 | proxy->clitcpka_intvl = tcpka_intvl; |
1132 | 0 | } else if (strcmp(args[0], "srvtcpka-intvl") == 0) { |
1133 | 0 | if (!(proxy->cap & PR_CAP_BE)) { |
1134 | 0 | memprintf(err, "%s will be ignored because %s '%s' has no backend capability", |
1135 | 0 | args[0], proxy_type_str(proxy), proxy->id); |
1136 | 0 | retval = 1; |
1137 | 0 | } |
1138 | 0 | proxy->srvtcpka_intvl = tcpka_intvl; |
1139 | 0 | } else { |
1140 | | /* unreachable */ |
1141 | 0 | memprintf(err, "'%s': unknown keyword", args[0]); |
1142 | 0 | return -1; |
1143 | 0 | } |
1144 | | |
1145 | 0 | return retval; |
1146 | 0 | } |
1147 | | #endif |
1148 | | |
1149 | | static int proxy_parse_guid(char **args, int section_type, struct proxy *curpx, |
1150 | | const struct proxy *defpx, const char *file, int line, |
1151 | | char **err) |
1152 | 0 | { |
1153 | 0 | const char *guid; |
1154 | 0 | char *guid_err = NULL; |
1155 | |
|
1156 | 0 | if (curpx->cap & PR_CAP_DEF) { |
1157 | 0 | ha_alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, line, args[0]); |
1158 | 0 | return -1; |
1159 | 0 | } |
1160 | | |
1161 | 0 | if (!*args[1]) { |
1162 | 0 | memprintf(err, "'%s' : expects an argument", args[0]); |
1163 | 0 | return -1; |
1164 | 0 | } |
1165 | | |
1166 | 0 | guid = args[1]; |
1167 | 0 | if (guid_insert(&curpx->obj_type, guid, &guid_err)) { |
1168 | 0 | memprintf(err, "'%s': %s", args[0], guid_err); |
1169 | 0 | ha_free(&guid_err); |
1170 | 0 | return -1; |
1171 | 0 | } |
1172 | | |
1173 | 0 | return 0; |
1174 | 0 | } |
1175 | | |
1176 | | /* This function inserts proxy <px> into the tree of known proxies (regular |
1177 | | * ones or defaults depending on px->cap & PR_CAP_DEF). The proxy's name is |
1178 | | * used as the storing key so it must already have been initialized. |
1179 | | */ |
1180 | | void proxy_store_name(struct proxy *px) |
1181 | 0 | { |
1182 | 0 | struct eb_root *root = (px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name; |
1183 | |
|
1184 | 0 | px->conf.by_name.key = px->id; |
1185 | 0 | ebis_insert(root, &px->conf.by_name); |
1186 | 0 | } |
1187 | | |
1188 | | /* Returns a pointer to the first proxy matching capabilities <cap> and id |
1189 | | * <id>. NULL is returned if no match is found. If <table> is non-zero, it |
1190 | | * only considers proxies having a table. |
1191 | | */ |
1192 | | struct proxy *proxy_find_by_id(int id, int cap, int table) |
1193 | 0 | { |
1194 | 0 | struct eb32_node *n; |
1195 | |
|
1196 | 0 | for (n = eb32_lookup(&used_proxy_id, id); n; n = eb32_next(n)) { |
1197 | 0 | struct proxy *px = container_of(n, struct proxy, conf.id); |
1198 | |
|
1199 | 0 | if (px->uuid != id) |
1200 | 0 | break; |
1201 | | |
1202 | 0 | if ((px->cap & cap) != cap) |
1203 | 0 | continue; |
1204 | | |
1205 | 0 | if (table && (!px->table || !px->table->size)) |
1206 | 0 | continue; |
1207 | | |
1208 | 0 | return px; |
1209 | 0 | } |
1210 | 0 | return NULL; |
1211 | 0 | } |
1212 | | |
1213 | | /* Returns a pointer to the first proxy matching either name <name>, or id |
1214 | | * <name> if <name> begins with a '#'. NULL is returned if no match is found. |
1215 | | * If <table> is non-zero, it only considers proxies having a table. The search |
1216 | | * is made into the regular proxies, unless <cap> has PR_CAP_DEF set in which |
1217 | | * case it's searched into the defproxy tree. |
1218 | | */ |
1219 | | struct proxy *proxy_find_by_name(const char *name, int cap, int table) |
1220 | 0 | { |
1221 | 0 | struct proxy *curproxy; |
1222 | |
|
1223 | 0 | if (*name == '#' && !(cap & PR_CAP_DEF)) { |
1224 | 0 | curproxy = proxy_find_by_id(atoi(name + 1), cap, table); |
1225 | 0 | if (curproxy) |
1226 | 0 | return curproxy; |
1227 | 0 | } |
1228 | 0 | else { |
1229 | 0 | struct eb_root *root; |
1230 | 0 | struct ebpt_node *node; |
1231 | |
|
1232 | 0 | root = (cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name; |
1233 | 0 | for (node = ebis_lookup(root, name); node; node = ebpt_next(node)) { |
1234 | 0 | curproxy = container_of(node, struct proxy, conf.by_name); |
1235 | |
|
1236 | 0 | if (strcmp(curproxy->id, name) != 0) |
1237 | 0 | break; |
1238 | | |
1239 | 0 | if ((curproxy->cap & cap) != cap) |
1240 | 0 | continue; |
1241 | | |
1242 | 0 | if (table && (!curproxy->table || !curproxy->table->size)) |
1243 | 0 | continue; |
1244 | | |
1245 | 0 | return curproxy; |
1246 | 0 | } |
1247 | 0 | } |
1248 | 0 | return NULL; |
1249 | 0 | } |
1250 | | |
1251 | | /* Finds the best match for a proxy with capabilities <cap>, name <name> and id |
1252 | | * <id>. At most one of <id> or <name> may be different provided that <cap> is |
1253 | | * valid. Either <id> or <name> may be left unspecified (0). The purpose is to |
1254 | | * find a proxy based on some information from a previous configuration, across |
1255 | | * reloads or during information exchange between peers. |
1256 | | * |
1257 | | * Names are looked up first if present, then IDs are compared if present. In |
1258 | | * case of an inexact match whatever is forced in the configuration has |
1259 | | * precedence in the following order : |
1260 | | * - 1) forced ID (proves a renaming / change of proxy type) |
1261 | | * - 2) proxy name+type (may indicate a move if ID differs) |
1262 | | * - 3) automatic ID+type (may indicate a renaming) |
1263 | | * |
1264 | | * Depending on what is found, we can end up in the following situations : |
1265 | | * |
1266 | | * name id cap | possible causes |
1267 | | * -------------+----------------- |
1268 | | * -- -- -- | nothing found |
1269 | | * -- -- ok | nothing found |
1270 | | * -- ok -- | proxy deleted, ID points to next one |
1271 | | * -- ok ok | proxy renamed, or deleted with ID pointing to next one |
1272 | | * ok -- -- | proxy deleted, but other half with same name still here (before) |
1273 | | * ok -- ok | proxy's ID changed (proxy moved in the config file) |
1274 | | * ok ok -- | proxy deleted, but other half with same name still here (after) |
1275 | | * ok ok ok | perfect match |
1276 | | * |
1277 | | * Upon return if <diff> is not NULL, it is zeroed then filled with up to 3 bits : |
1278 | | * - PR_FBM_MISMATCH_ID : proxy was found but ID differs |
1279 | | * (and ID was not zero) |
1280 | | * - PR_FBM_MISMATCH_NAME : proxy was found by ID but name differs |
1281 | | * (and name was not NULL) |
1282 | | * - PR_FBM_MISMATCH_PROXYTYPE : a proxy of different type was found with |
1283 | | * the same name and/or id |
1284 | | * |
1285 | | * Only a valid proxy is returned. If capabilities do not match, NULL is |
1286 | | * returned. The caller can check <diff> to report detailed warnings / errors, |
1287 | | * and decide whether or not to use what was found. |
1288 | | */ |
1289 | | struct proxy *proxy_find_best_match(int cap, const char *name, int id, int *diff) |
1290 | 0 | { |
1291 | 0 | struct proxy *byname; |
1292 | 0 | struct proxy *byid; |
1293 | |
|
1294 | 0 | if (!name && !id) |
1295 | 0 | return NULL; |
1296 | | |
1297 | 0 | if (diff) |
1298 | 0 | *diff = 0; |
1299 | |
|
1300 | 0 | byname = byid = NULL; |
1301 | |
|
1302 | 0 | if (name) { |
1303 | 0 | byname = proxy_find_by_name(name, cap, 0); |
1304 | 0 | if (byname && (!id || byname->uuid == id)) |
1305 | 0 | return byname; |
1306 | 0 | } |
1307 | | |
1308 | | /* remaining possibilities : |
1309 | | * - name not set |
1310 | | * - name set but not found |
1311 | | * - name found, but ID doesn't match. |
1312 | | */ |
1313 | 0 | if (id) { |
1314 | 0 | byid = proxy_find_by_id(id, cap, 0); |
1315 | 0 | if (byid) { |
1316 | 0 | if (byname) { |
1317 | | /* id+type found, name+type found, but not all 3. |
1318 | | * ID wins only if forced, otherwise name wins. |
1319 | | */ |
1320 | 0 | if (byid->options & PR_O_FORCED_ID) { |
1321 | 0 | if (diff) |
1322 | 0 | *diff |= PR_FBM_MISMATCH_NAME; |
1323 | 0 | return byid; |
1324 | 0 | } |
1325 | 0 | else { |
1326 | 0 | if (diff) |
1327 | 0 | *diff |= PR_FBM_MISMATCH_ID; |
1328 | 0 | return byname; |
1329 | 0 | } |
1330 | 0 | } |
1331 | | |
1332 | | /* remaining possibilities : |
1333 | | * - name not set |
1334 | | * - name set but not found |
1335 | | */ |
1336 | 0 | if (name && diff) |
1337 | 0 | *diff |= PR_FBM_MISMATCH_NAME; |
1338 | 0 | return byid; |
1339 | 0 | } |
1340 | | |
1341 | | /* ID not found */ |
1342 | 0 | if (byname) { |
1343 | 0 | if (diff) |
1344 | 0 | *diff |= PR_FBM_MISMATCH_ID; |
1345 | 0 | return byname; |
1346 | 0 | } |
1347 | 0 | } |
1348 | | |
1349 | | /* All remaining possibilities will lead to NULL. If we can report more |
1350 | | * detailed information to the caller about changed types and/or name, |
1351 | | * we'll do it. For example, we could detect that "listen foo" was |
1352 | | * split into "frontend foo_ft" and "backend foo_bk" if IDs are forced. |
1353 | | * - name not set, ID not found |
1354 | | * - name not found, ID not set |
1355 | | * - name not found, ID not found |
1356 | | */ |
1357 | 0 | if (!diff) |
1358 | 0 | return NULL; |
1359 | | |
1360 | 0 | if (name) { |
1361 | 0 | byname = proxy_find_by_name(name, 0, 0); |
1362 | 0 | if (byname && (!id || byname->uuid == id)) |
1363 | 0 | *diff |= PR_FBM_MISMATCH_PROXYTYPE; |
1364 | 0 | } |
1365 | |
|
1366 | 0 | if (id) { |
1367 | 0 | byid = proxy_find_by_id(id, 0, 0); |
1368 | 0 | if (byid) { |
1369 | 0 | if (!name) |
1370 | 0 | *diff |= PR_FBM_MISMATCH_PROXYTYPE; /* only type changed */ |
1371 | 0 | else if (byid->options & PR_O_FORCED_ID) |
1372 | 0 | *diff |= PR_FBM_MISMATCH_NAME | PR_FBM_MISMATCH_PROXYTYPE; /* name and type changed */ |
1373 | | /* otherwise it's a different proxy that was returned */ |
1374 | 0 | } |
1375 | 0 | } |
1376 | 0 | return NULL; |
1377 | 0 | } |
1378 | | |
1379 | | /* |
1380 | | * This function finds a server with matching name within selected proxy. |
1381 | | * It also checks if there are more matching servers with |
1382 | | * requested name as this often leads into unexpected situations. |
1383 | | */ |
1384 | | |
1385 | 0 | struct server *findserver(const struct proxy *px, const char *name) { |
1386 | |
|
1387 | 0 | struct server *cursrv, *target = NULL; |
1388 | |
|
1389 | 0 | if (!px) |
1390 | 0 | return NULL; |
1391 | | |
1392 | 0 | for (cursrv = px->srv; cursrv; cursrv = cursrv->next) { |
1393 | 0 | if (strcmp(cursrv->id, name) != 0) |
1394 | 0 | continue; |
1395 | | |
1396 | 0 | if (!target) { |
1397 | 0 | target = cursrv; |
1398 | 0 | continue; |
1399 | 0 | } |
1400 | | |
1401 | 0 | ha_alert("Refusing to use duplicated server '%s' found in proxy: %s!\n", |
1402 | 0 | name, px->id); |
1403 | |
|
1404 | 0 | return NULL; |
1405 | 0 | } |
1406 | | |
1407 | 0 | return target; |
1408 | 0 | } |
1409 | | |
1410 | | /* This function checks that the designated proxy has no http directives |
1411 | | * enabled. It will output a warning if there are, and will fix some of them. |
1412 | | * It returns the number of fatal errors encountered. This should be called |
1413 | | * at the end of the configuration parsing if the proxy is not in http mode. |
1414 | | * The <file> argument is used to construct the error message. |
1415 | | */ |
1416 | | int proxy_cfg_ensure_no_http(struct proxy *curproxy) |
1417 | 0 | { |
1418 | 0 | if (curproxy->cookie_name != NULL) { |
1419 | 0 | ha_warning("cookie will be ignored for %s '%s' (needs 'mode http').\n", |
1420 | 0 | proxy_type_str(curproxy), curproxy->id); |
1421 | 0 | } |
1422 | 0 | if (curproxy->lbprm.algo & BE_LB_NEED_HTTP) { |
1423 | 0 | curproxy->lbprm.algo &= ~BE_LB_ALGO; |
1424 | 0 | curproxy->lbprm.algo |= BE_LB_ALGO_RR; |
1425 | 0 | ha_warning("Layer 7 hash not possible for %s '%s' (needs 'mode http'). Falling back to round robin.\n", |
1426 | 0 | proxy_type_str(curproxy), curproxy->id); |
1427 | 0 | } |
1428 | 0 | if (curproxy->logformat.str == default_http_log_format) { |
1429 | | /* Note: we don't change the directive's file:line number */ |
1430 | 0 | curproxy->logformat.str = default_tcp_log_format; |
1431 | 0 | ha_warning("parsing [%s:%d] : 'option httplog' not usable with %s '%s' (needs 'mode http'). Falling back to 'option tcplog'.\n", |
1432 | 0 | curproxy->logformat.conf.file, curproxy->logformat.conf.line, |
1433 | 0 | proxy_type_str(curproxy), curproxy->id); |
1434 | 0 | } |
1435 | 0 | else if (curproxy->logformat.str == clf_http_log_format) { |
1436 | | /* Note: we don't change the directive's file:line number */ |
1437 | 0 | curproxy->logformat.str = clf_tcp_log_format; |
1438 | 0 | ha_warning("parsing [%s:%d] : 'option httplog clf' not usable with %s '%s' (needs 'mode http'). Falling back to 'option tcplog clf'.\n", |
1439 | 0 | curproxy->logformat.conf.file, curproxy->logformat.conf.line, |
1440 | 0 | proxy_type_str(curproxy), curproxy->id); |
1441 | 0 | } |
1442 | 0 | else if (curproxy->logformat.str == default_https_log_format) { |
1443 | | /* Note: we don't change the directive's file:line number */ |
1444 | 0 | curproxy->logformat.str = default_tcp_log_format; |
1445 | 0 | ha_warning("parsing [%s:%d] : 'option httpslog' not usable with %s '%s' (needs 'mode http'). Falling back to 'option tcplog'.\n", |
1446 | 0 | curproxy->logformat.conf.file, curproxy->logformat.conf.line, |
1447 | 0 | proxy_type_str(curproxy), curproxy->id); |
1448 | 0 | } |
1449 | |
|
1450 | 0 | return 0; |
1451 | 0 | } |
1452 | | |
1453 | | /* This function checks that the designated proxy has no log directives |
1454 | | * enabled. It will output a warning if there are, and will fix some of them. |
1455 | | * It returns the number of fatal errors encountered. This should be called |
1456 | | * at the end of the configuration parsing if the proxy is not in log mode. |
1457 | | * The <file> argument is used to construct the error message. |
1458 | | */ |
1459 | | int proxy_cfg_ensure_no_log(struct proxy *curproxy) |
1460 | 0 | { |
1461 | 0 | if (curproxy->lbprm.algo & BE_LB_NEED_LOG) { |
1462 | 0 | curproxy->lbprm.algo &= ~BE_LB_ALGO; |
1463 | 0 | curproxy->lbprm.algo |= BE_LB_ALGO_RR; |
1464 | 0 | ha_warning("Unusable balance algorithm for %s '%s' (needs 'mode log'). Falling back to round robin.\n", |
1465 | 0 | proxy_type_str(curproxy), curproxy->id); |
1466 | 0 | } |
1467 | |
|
1468 | 0 | return 0; |
1469 | 0 | } |
1470 | | |
1471 | | /* Perform the most basic initialization of a proxy : |
1472 | | * memset(), list_init(*), reset_timeouts(*). |
1473 | | * Any new proxy or peer should be initialized via this function. |
1474 | | */ |
1475 | | void init_new_proxy(struct proxy *p) |
1476 | 0 | { |
1477 | 0 | memset(p, 0, sizeof(struct proxy)); |
1478 | 0 | p->obj_type = OBJ_TYPE_PROXY; |
1479 | 0 | LIST_INIT(&p->global_list); |
1480 | 0 | LIST_INIT(&p->acl); |
1481 | 0 | LIST_INIT(&p->http_req_rules); |
1482 | 0 | LIST_INIT(&p->http_res_rules); |
1483 | 0 | LIST_INIT(&p->http_after_res_rules); |
1484 | 0 | LIST_INIT(&p->redirect_rules); |
1485 | 0 | LIST_INIT(&p->mon_fail_cond); |
1486 | 0 | LIST_INIT(&p->switching_rules); |
1487 | 0 | LIST_INIT(&p->server_rules); |
1488 | 0 | LIST_INIT(&p->persist_rules); |
1489 | 0 | LIST_INIT(&p->sticking_rules); |
1490 | 0 | LIST_INIT(&p->storersp_rules); |
1491 | 0 | LIST_INIT(&p->tcp_req.inspect_rules); |
1492 | 0 | LIST_INIT(&p->tcp_rep.inspect_rules); |
1493 | 0 | LIST_INIT(&p->tcp_req.l4_rules); |
1494 | 0 | LIST_INIT(&p->tcp_req.l5_rules); |
1495 | | #ifdef USE_QUIC |
1496 | | LIST_INIT(&p->quic_init_rules); |
1497 | | #endif |
1498 | 0 | MT_LIST_INIT(&p->listener_queue); |
1499 | 0 | LIST_INIT(&p->loggers); |
1500 | 0 | LIST_INIT(&p->conf.bind); |
1501 | 0 | LIST_INIT(&p->conf.listeners); |
1502 | 0 | LIST_INIT(&p->conf.errors); |
1503 | 0 | LIST_INIT(&p->conf.args.list); |
1504 | 0 | LIST_INIT(&p->conf.lf_checks); |
1505 | 0 | LIST_INIT(&p->filter_configs); |
1506 | 0 | LIST_INIT(&p->tcpcheck_rules.preset_vars); |
1507 | |
|
1508 | 0 | MT_LIST_INIT(&p->lbprm.lb_free_list); |
1509 | |
|
1510 | 0 | p->defsrv.id = "default-server"; |
1511 | 0 | p->conf.used_listener_id = EB_ROOT; |
1512 | 0 | p->conf.used_server_id = EB_ROOT; |
1513 | 0 | p->used_server_addr = EB_ROOT_UNIQUE; |
1514 | 0 | p->conf.log_steps = EB_ROOT_UNIQUE; |
1515 | | |
1516 | | /* Timeouts are defined as -1 */ |
1517 | 0 | proxy_reset_timeouts(p); |
1518 | 0 | p->tcp_rep.inspect_delay = TICK_ETERNITY; |
1519 | | |
1520 | | /* initial uuid is unassigned (-1) */ |
1521 | 0 | p->uuid = -1; |
1522 | | |
1523 | | /* Default to only allow L4 retries */ |
1524 | 0 | p->retry_type = PR_RE_CONN_FAILED; |
1525 | |
|
1526 | 0 | guid_init(&p->guid); |
1527 | |
|
1528 | 0 | p->extra_counters_fe = NULL; |
1529 | 0 | p->extra_counters_be = NULL; |
1530 | |
|
1531 | 0 | HA_RWLOCK_INIT(&p->lock); |
1532 | | |
1533 | | /* initialize the default settings */ |
1534 | 0 | proxy_preset_defaults(p); |
1535 | 0 | } |
1536 | | |
1537 | | /* Initialize per-thread proxy fields */ |
1538 | | int proxy_init_per_thr(struct proxy *px) |
1539 | 0 | { |
1540 | 0 | int i; |
1541 | |
|
1542 | 0 | px->per_tgrp = calloc(global.nbtgroups, sizeof(*px->per_tgrp)); |
1543 | 0 | for (i = 0; i < global.nbtgroups; i++) |
1544 | 0 | queue_init(&px->per_tgrp[i].queue, px, NULL); |
1545 | |
|
1546 | 0 | return 0; |
1547 | 0 | } |
1548 | | |
1549 | | /* Preset default settings onto proxy <defproxy>. */ |
1550 | | void proxy_preset_defaults(struct proxy *defproxy) |
1551 | 0 | { |
1552 | 0 | defproxy->mode = PR_MODE_TCP; |
1553 | 0 | defproxy->flags = 0; |
1554 | 0 | if (!(defproxy->cap & PR_CAP_INT)) { |
1555 | 0 | defproxy->maxconn = cfg_maxpconn; |
1556 | 0 | defproxy->conn_retries = CONN_RETRIES; |
1557 | 0 | } |
1558 | 0 | defproxy->redispatch_after = 0; |
1559 | 0 | defproxy->options = PR_O_REUSE_SAFE; |
1560 | 0 | if (defproxy->cap & PR_CAP_INT) |
1561 | 0 | defproxy->options2 |= PR_O2_INDEPSTR; |
1562 | 0 | defproxy->max_out_conns = MAX_SRV_LIST; |
1563 | |
|
1564 | 0 | srv_settings_init(&defproxy->defsrv); |
1565 | |
|
1566 | 0 | lf_expr_init(&defproxy->logformat); |
1567 | 0 | lf_expr_init(&defproxy->logformat_sd); |
1568 | 0 | lf_expr_init(&defproxy->format_unique_id); |
1569 | 0 | lf_expr_init(&defproxy->logformat_error); |
1570 | |
|
1571 | 0 | defproxy->email_alert.level = LOG_ALERT; |
1572 | 0 | defproxy->load_server_state_from_file = PR_SRV_STATE_FILE_UNSPEC; |
1573 | |
|
1574 | 0 | if (defproxy->cap & PR_CAP_INT) |
1575 | 0 | defproxy->timeout.connect = 5000; |
1576 | 0 | } |
1577 | | |
1578 | | /* Frees all dynamic settings allocated on a default proxy that's about to be |
1579 | | * destroyed. Note that most of the fields are not even reset, so extreme care |
1580 | | * is required here, and calling proxy_preset_defaults() afterwards would be |
1581 | | * safer. |
1582 | | */ |
1583 | | void proxy_free_defaults(struct proxy *defproxy) |
1584 | 0 | { |
1585 | 0 | struct cap_hdr *h,*h_next; |
1586 | |
|
1587 | 0 | proxy_free_common(defproxy); |
1588 | | |
1589 | | /* default proxy specific cleanup */ |
1590 | 0 | ha_free((char **)&defproxy->defsrv.conf.file); |
1591 | 0 | ha_free(&defproxy->defbe.name); |
1592 | |
|
1593 | 0 | h = defproxy->req_cap; |
1594 | 0 | while (h) { |
1595 | 0 | h_next = h->next; |
1596 | 0 | free(h->name); |
1597 | 0 | pool_destroy(h->pool); |
1598 | 0 | free(h); |
1599 | 0 | h = h_next; |
1600 | 0 | } |
1601 | |
|
1602 | 0 | h = defproxy->rsp_cap; |
1603 | 0 | while (h) { |
1604 | 0 | h_next = h->next; |
1605 | 0 | free(h->name); |
1606 | 0 | pool_destroy(h->pool); |
1607 | 0 | free(h); |
1608 | 0 | h = h_next; |
1609 | 0 | } |
1610 | |
|
1611 | 0 | proxy_release_conf_errors(defproxy); |
1612 | 0 | deinit_proxy_tcpcheck(defproxy); |
1613 | 0 | } |
1614 | | |
1615 | | /* delete a defproxy from the tree if still in it, frees its content and its |
1616 | | * storage. Nothing is done if <px> is NULL or if it doesn't have PR_CAP_DEF |
1617 | | * set, allowing to pass it the direct result of a lookup function. |
1618 | | */ |
1619 | | void proxy_destroy_defaults(struct proxy *px) |
1620 | 0 | { |
1621 | 0 | if (!px) |
1622 | 0 | return; |
1623 | 0 | if (!(px->cap & PR_CAP_DEF)) |
1624 | 0 | return; |
1625 | 0 | BUG_ON(px->conf.refcount != 0); |
1626 | 0 | ebpt_delete(&px->conf.by_name); |
1627 | 0 | proxy_free_defaults(px); |
1628 | 0 | free(px); |
1629 | 0 | } |
1630 | | |
1631 | | /* delete all unreferenced default proxies. A default proxy is unreferenced if |
1632 | | * its refcount is equal to zero. |
1633 | | */ |
1634 | | void proxy_destroy_all_unref_defaults() |
1635 | 0 | { |
1636 | 0 | struct ebpt_node *n; |
1637 | 0 | struct proxy *px, *nx; |
1638 | |
|
1639 | 0 | n = ebpt_first(&defproxy_by_name); |
1640 | 0 | while (n) { |
1641 | 0 | px = container_of(n, struct proxy, conf.by_name); |
1642 | 0 | BUG_ON(!(px->cap & PR_CAP_DEF)); |
1643 | 0 | n = ebpt_next(n); |
1644 | 0 | if (!px->conf.refcount) |
1645 | 0 | proxy_destroy_defaults(px); |
1646 | 0 | } |
1647 | | |
1648 | 0 | px = orphaned_default_proxies; |
1649 | 0 | while (px) { |
1650 | 0 | BUG_ON(!(px->cap & PR_CAP_DEF)); |
1651 | 0 | nx = px->next; |
1652 | 0 | if (!px->conf.refcount) |
1653 | 0 | proxy_destroy_defaults(px); |
1654 | 0 | px = nx; |
1655 | 0 | } |
1656 | 0 | } |
1657 | | |
1658 | | /* Try to destroy a defaults section, or just unreference it if still |
1659 | | * refcounted. In this case it's added to the orphaned_default_proxies list |
1660 | | * so that it can later be found. |
1661 | | */ |
1662 | | void proxy_unref_or_destroy_defaults(struct proxy *px) |
1663 | 0 | { |
1664 | 0 | if (!px || !(px->cap & PR_CAP_DEF)) |
1665 | 0 | return; |
1666 | | |
1667 | 0 | ebpt_delete(&px->conf.by_name); |
1668 | 0 | if (px->conf.refcount) { |
1669 | | /* still referenced just append it to the orphaned list */ |
1670 | 0 | px->next = orphaned_default_proxies; |
1671 | 0 | orphaned_default_proxies = px; |
1672 | 0 | } else |
1673 | 0 | proxy_destroy_defaults(px); |
1674 | 0 | } |
1675 | | |
1676 | | /* Add a reference on the default proxy <defpx> for the proxy <px> Nothing is |
1677 | | * done if <px> already references <defpx>. Otherwise, the default proxy |
1678 | | * refcount is incremented by one. For now, this operation is not thread safe |
1679 | | * and is perform during init stage only. |
1680 | | */ |
1681 | | void proxy_ref_defaults(struct proxy *px, struct proxy *defpx) |
1682 | 0 | { |
1683 | 0 | if (px->defpx == defpx) |
1684 | 0 | return; |
1685 | 0 | BUG_ON(px->defpx != NULL); |
1686 | 0 | px->defpx = defpx; |
1687 | 0 | defpx->conf.refcount++; |
1688 | 0 | } |
1689 | | |
1690 | | /* proxy <px> removes its reference on its default proxy. The default proxy |
1691 | | * refcount is decremented by one. If it was the last reference, the |
1692 | | * corresponding default proxy is destroyed. For now this operation is not |
1693 | | * thread safe and is performed during deinit staged only. |
1694 | | */ |
1695 | | void proxy_unref_defaults(struct proxy *px) |
1696 | 0 | { |
1697 | 0 | if (px->defpx == NULL) |
1698 | 0 | return; |
1699 | 0 | if (!--px->defpx->conf.refcount) |
1700 | 0 | proxy_destroy_defaults(px->defpx); |
1701 | 0 | px->defpx = NULL; |
1702 | 0 | } |
1703 | | |
1704 | | /* prepares a new proxy <name> of type <cap> from the provided <px> |
1705 | | * pointer. |
1706 | | * <px> is assumed to be freshly allocated |
1707 | | * <name> may be NULL: proxy id assigment will be skipped. |
1708 | | * |
1709 | | * Returns a 1 on success or 0 on failure (in which case errmsg must be checked |
1710 | | * then freed). |
1711 | | */ |
1712 | | int setup_new_proxy(struct proxy *px, const char *name, unsigned int cap, char **errmsg) |
1713 | 0 | { |
1714 | 0 | init_new_proxy(px); |
1715 | |
|
1716 | 0 | if (name) { |
1717 | 0 | px->id = strdup(name); |
1718 | 0 | if (!px->id) { |
1719 | 0 | memprintf(errmsg, "out of memory"); |
1720 | 0 | goto fail; |
1721 | 0 | } |
1722 | 0 | } |
1723 | | |
1724 | 0 | px->cap = cap; |
1725 | |
|
1726 | 0 | if (name && !(cap & PR_CAP_INT)) |
1727 | 0 | proxy_store_name(px); |
1728 | |
|
1729 | 0 | if (!(cap & PR_CAP_DEF)) |
1730 | 0 | LIST_APPEND(&proxies, &px->global_list); |
1731 | |
|
1732 | 0 | return 1; |
1733 | | |
1734 | 0 | fail: |
1735 | 0 | if (name) |
1736 | 0 | memprintf(errmsg, "proxy '%s': %s", name, *errmsg); |
1737 | |
|
1738 | 0 | ha_free(&px->id); |
1739 | 0 | counters_fe_shared_drop(px->fe_counters.shared); |
1740 | 0 | counters_be_shared_drop(px->be_counters.shared); |
1741 | |
|
1742 | 0 | return 0; |
1743 | 0 | } |
1744 | | |
1745 | | /* Allocates a new proxy <name> of type <cap>. |
1746 | | * Returns the proxy instance on success. On error, NULL is returned. |
1747 | | */ |
1748 | | struct proxy *alloc_new_proxy(const char *name, unsigned int cap, char **errmsg) |
1749 | 0 | { |
1750 | 0 | struct proxy *curproxy; |
1751 | |
|
1752 | 0 | if ((curproxy = calloc(1, sizeof(*curproxy))) == NULL) { |
1753 | 0 | memprintf(errmsg, "proxy '%s': out of memory", name); |
1754 | 0 | goto fail; |
1755 | 0 | } |
1756 | | |
1757 | 0 | if (!setup_new_proxy(curproxy, name, cap, errmsg)) |
1758 | 0 | goto fail; |
1759 | | |
1760 | 0 | done: |
1761 | 0 | return curproxy; |
1762 | | |
1763 | 0 | fail: |
1764 | | /* Note: in case of fatal error here, we WILL make valgrind unhappy, |
1765 | | * but its not worth trying to unroll everything here just before |
1766 | | * quitting. |
1767 | | */ |
1768 | 0 | free(curproxy); |
1769 | 0 | return NULL; |
1770 | 0 | } |
1771 | | |
1772 | | /* post-check for proxies */ |
1773 | | static int proxy_postcheck(struct proxy *px) |
1774 | 0 | { |
1775 | 0 | int err_code = ERR_NONE; |
1776 | | |
1777 | | /* allocate private memory for shared counters: used as a fallback |
1778 | | * or when sharing is disabled. If sharing is enabled pointers will |
1779 | | * be updated to point to the proper shared memory location during |
1780 | | * proxy postparsing, see proxy_postparse() |
1781 | | */ |
1782 | 0 | if (px->cap & PR_CAP_FE) { |
1783 | 0 | px->fe_counters.shared = counters_fe_shared_get(&px->guid); |
1784 | 0 | if (!px->fe_counters.shared) { |
1785 | 0 | ha_alert("out of memory while setting up shared counters for %s %s\n", |
1786 | 0 | proxy_type_str(px), px->id); |
1787 | 0 | err_code |= ERR_ALERT | ERR_FATAL; |
1788 | 0 | goto out; |
1789 | 0 | } |
1790 | 0 | } |
1791 | 0 | if (px->cap & (PR_CAP_FE|PR_CAP_BE)) { |
1792 | | /* by default stream->be points to stream->fe, thus proxy |
1793 | | * be_counters may be used even if the proxy lacks the backend |
1794 | | * capability |
1795 | | */ |
1796 | 0 | px->be_counters.shared = counters_be_shared_get(&px->guid); |
1797 | 0 | if (!px->be_counters.shared) { |
1798 | 0 | ha_alert("out of memory while setting up shared counters for %s %s\n", |
1799 | 0 | proxy_type_str(px), px->id); |
1800 | 0 | err_code |= ERR_ALERT | ERR_FATAL; |
1801 | 0 | goto out; |
1802 | 0 | } |
1803 | |
|
1804 | 0 | } |
1805 | | |
1806 | | |
1807 | 0 | out: |
1808 | 0 | return err_code; |
1809 | 0 | } |
1810 | | REGISTER_POST_PROXY_CHECK(proxy_postcheck); |
1811 | | |
1812 | | /* Copy the proxy settings from <defproxy> to <curproxy>. |
1813 | | * Returns 0 on success. |
1814 | | * Returns 1 on error. <errmsg> will be allocated with an error description. |
1815 | | */ |
1816 | | static int proxy_defproxy_cpy(struct proxy *curproxy, const struct proxy *defproxy, |
1817 | | char **errmsg) |
1818 | 0 | { |
1819 | 0 | struct logger *tmplogger; |
1820 | 0 | char *tmpmsg = NULL; |
1821 | 0 | struct eb32_node *node; |
1822 | | |
1823 | | /* set default values from the specified default proxy */ |
1824 | 0 | srv_settings_cpy(&curproxy->defsrv, &defproxy->defsrv, 0); |
1825 | |
|
1826 | 0 | curproxy->flags = (defproxy->flags & PR_FL_DISABLED); /* Only inherit from disabled flag */ |
1827 | 0 | curproxy->options = defproxy->options; |
1828 | 0 | curproxy->options2 = defproxy->options2; |
1829 | 0 | curproxy->no_options = defproxy->no_options; |
1830 | 0 | curproxy->no_options2 = defproxy->no_options2; |
1831 | 0 | curproxy->retry_type = defproxy->retry_type; |
1832 | 0 | curproxy->tcp_req.inspect_delay = defproxy->tcp_req.inspect_delay; |
1833 | 0 | curproxy->tcp_rep.inspect_delay = defproxy->tcp_rep.inspect_delay; |
1834 | |
|
1835 | 0 | http_ext_clean(curproxy); |
1836 | 0 | http_ext_dup(defproxy, curproxy); |
1837 | |
|
1838 | 0 | if (isttest(defproxy->server_id_hdr_name)) |
1839 | 0 | curproxy->server_id_hdr_name = istdup(defproxy->server_id_hdr_name); |
1840 | | |
1841 | | /* initialize error relocations */ |
1842 | 0 | if (!proxy_dup_default_conf_errors(curproxy, defproxy, &tmpmsg)) { |
1843 | 0 | memprintf(errmsg, "proxy '%s' : %s", curproxy->id, tmpmsg); |
1844 | 0 | free(tmpmsg); |
1845 | 0 | return 1; |
1846 | 0 | } |
1847 | | |
1848 | 0 | if (curproxy->cap & PR_CAP_FE) { |
1849 | 0 | curproxy->maxconn = defproxy->maxconn; |
1850 | 0 | curproxy->backlog = defproxy->backlog; |
1851 | 0 | curproxy->fe_sps_lim = defproxy->fe_sps_lim; |
1852 | |
|
1853 | 0 | curproxy->to_log = defproxy->to_log & ~LW_COOKIE & ~LW_REQHDR & ~ LW_RSPHDR; |
1854 | 0 | curproxy->max_out_conns = defproxy->max_out_conns; |
1855 | |
|
1856 | 0 | curproxy->clitcpka_cnt = defproxy->clitcpka_cnt; |
1857 | 0 | curproxy->clitcpka_idle = defproxy->clitcpka_idle; |
1858 | 0 | curproxy->clitcpka_intvl = defproxy->clitcpka_intvl; |
1859 | 0 | } |
1860 | |
|
1861 | 0 | if (curproxy->cap & PR_CAP_BE) { |
1862 | 0 | curproxy->lbprm.algo = defproxy->lbprm.algo; |
1863 | 0 | curproxy->lbprm.hash_balance_factor = defproxy->lbprm.hash_balance_factor; |
1864 | 0 | curproxy->fullconn = defproxy->fullconn; |
1865 | 0 | curproxy->conn_retries = defproxy->conn_retries; |
1866 | 0 | curproxy->redispatch_after = defproxy->redispatch_after; |
1867 | 0 | curproxy->max_ka_queue = defproxy->max_ka_queue; |
1868 | |
|
1869 | 0 | curproxy->tcpcheck_rules.flags = (defproxy->tcpcheck_rules.flags & ~TCPCHK_RULES_UNUSED_RS); |
1870 | 0 | curproxy->tcpcheck_rules.list = defproxy->tcpcheck_rules.list; |
1871 | 0 | if (!LIST_ISEMPTY(&defproxy->tcpcheck_rules.preset_vars)) { |
1872 | 0 | if (!dup_tcpcheck_vars(&curproxy->tcpcheck_rules.preset_vars, |
1873 | 0 | &defproxy->tcpcheck_rules.preset_vars)) { |
1874 | 0 | memprintf(errmsg, "proxy '%s': failed to duplicate tcpcheck preset-vars", curproxy->id); |
1875 | 0 | return 1; |
1876 | 0 | } |
1877 | 0 | } |
1878 | | |
1879 | 0 | curproxy->ck_opts = defproxy->ck_opts; |
1880 | |
|
1881 | 0 | if (defproxy->cookie_name) |
1882 | 0 | curproxy->cookie_name = strdup(defproxy->cookie_name); |
1883 | 0 | curproxy->cookie_len = defproxy->cookie_len; |
1884 | |
|
1885 | 0 | if (defproxy->dyncookie_key) |
1886 | 0 | curproxy->dyncookie_key = strdup(defproxy->dyncookie_key); |
1887 | 0 | if (defproxy->cookie_domain) |
1888 | 0 | curproxy->cookie_domain = strdup(defproxy->cookie_domain); |
1889 | |
|
1890 | 0 | if (defproxy->cookie_maxidle) |
1891 | 0 | curproxy->cookie_maxidle = defproxy->cookie_maxidle; |
1892 | |
|
1893 | 0 | if (defproxy->cookie_maxlife) |
1894 | 0 | curproxy->cookie_maxlife = defproxy->cookie_maxlife; |
1895 | |
|
1896 | 0 | if (defproxy->rdp_cookie_name) |
1897 | 0 | curproxy->rdp_cookie_name = strdup(defproxy->rdp_cookie_name); |
1898 | 0 | curproxy->rdp_cookie_len = defproxy->rdp_cookie_len; |
1899 | |
|
1900 | 0 | if (defproxy->cookie_attrs) |
1901 | 0 | curproxy->cookie_attrs = strdup(defproxy->cookie_attrs); |
1902 | |
|
1903 | 0 | if (defproxy->lbprm.arg_str) |
1904 | 0 | curproxy->lbprm.arg_str = strdup(defproxy->lbprm.arg_str); |
1905 | 0 | curproxy->lbprm.arg_len = defproxy->lbprm.arg_len; |
1906 | 0 | curproxy->lbprm.arg_opt1 = defproxy->lbprm.arg_opt1; |
1907 | 0 | curproxy->lbprm.arg_opt2 = defproxy->lbprm.arg_opt2; |
1908 | 0 | curproxy->lbprm.arg_opt3 = defproxy->lbprm.arg_opt3; |
1909 | |
|
1910 | 0 | if (defproxy->conn_src.iface_name) |
1911 | 0 | curproxy->conn_src.iface_name = strdup(defproxy->conn_src.iface_name); |
1912 | 0 | curproxy->conn_src.iface_len = defproxy->conn_src.iface_len; |
1913 | 0 | curproxy->conn_src.opts = defproxy->conn_src.opts; |
1914 | 0 | #if defined(CONFIG_HAP_TRANSPARENT) |
1915 | 0 | curproxy->conn_src.tproxy_addr = defproxy->conn_src.tproxy_addr; |
1916 | 0 | #endif |
1917 | 0 | curproxy->load_server_state_from_file = defproxy->load_server_state_from_file; |
1918 | |
|
1919 | 0 | curproxy->srvtcpka_cnt = defproxy->srvtcpka_cnt; |
1920 | 0 | curproxy->srvtcpka_idle = defproxy->srvtcpka_idle; |
1921 | 0 | curproxy->srvtcpka_intvl = defproxy->srvtcpka_intvl; |
1922 | 0 | } |
1923 | | |
1924 | 0 | if (curproxy->cap & PR_CAP_FE) { |
1925 | 0 | if (defproxy->capture_name) |
1926 | 0 | curproxy->capture_name = strdup(defproxy->capture_name); |
1927 | 0 | curproxy->capture_namelen = defproxy->capture_namelen; |
1928 | 0 | curproxy->capture_len = defproxy->capture_len; |
1929 | |
|
1930 | 0 | curproxy->nb_req_cap = defproxy->nb_req_cap; |
1931 | 0 | curproxy->req_cap = defproxy->req_cap; |
1932 | |
|
1933 | 0 | curproxy->nb_rsp_cap = defproxy->nb_rsp_cap; |
1934 | 0 | curproxy->rsp_cap = defproxy->rsp_cap; |
1935 | 0 | } |
1936 | |
|
1937 | 0 | if (curproxy->cap & PR_CAP_FE) { |
1938 | 0 | curproxy->timeout.client = defproxy->timeout.client; |
1939 | 0 | curproxy->timeout.client_hs = defproxy->timeout.client_hs; |
1940 | 0 | curproxy->timeout.clientfin = defproxy->timeout.clientfin; |
1941 | 0 | curproxy->timeout.tarpit = defproxy->timeout.tarpit; |
1942 | 0 | curproxy->timeout.httpreq = defproxy->timeout.httpreq; |
1943 | 0 | curproxy->timeout.httpka = defproxy->timeout.httpka; |
1944 | 0 | if (isttest(defproxy->monitor_uri)) |
1945 | 0 | curproxy->monitor_uri = istdup(defproxy->monitor_uri); |
1946 | 0 | if (defproxy->defbe.name) |
1947 | 0 | curproxy->defbe.name = strdup(defproxy->defbe.name); |
1948 | |
|
1949 | 0 | lf_expr_dup(&defproxy->logformat, &curproxy->logformat); |
1950 | 0 | lf_expr_dup(&defproxy->logformat_sd, &curproxy->logformat_sd); |
1951 | 0 | lf_expr_dup(&defproxy->logformat_error, &curproxy->logformat_error); |
1952 | 0 | } |
1953 | |
|
1954 | 0 | if (curproxy->cap & PR_CAP_BE) { |
1955 | 0 | curproxy->timeout.connect = defproxy->timeout.connect; |
1956 | 0 | curproxy->timeout.server = defproxy->timeout.server; |
1957 | 0 | curproxy->timeout.serverfin = defproxy->timeout.serverfin; |
1958 | 0 | curproxy->timeout.check = defproxy->timeout.check; |
1959 | 0 | curproxy->timeout.queue = defproxy->timeout.queue; |
1960 | 0 | curproxy->timeout.tarpit = defproxy->timeout.tarpit; |
1961 | 0 | curproxy->timeout.httpreq = defproxy->timeout.httpreq; |
1962 | 0 | curproxy->timeout.httpka = defproxy->timeout.httpka; |
1963 | 0 | curproxy->timeout.tunnel = defproxy->timeout.tunnel; |
1964 | 0 | curproxy->conn_src.source_addr = defproxy->conn_src.source_addr; |
1965 | 0 | } |
1966 | |
|
1967 | 0 | curproxy->mode = defproxy->mode; |
1968 | | |
1969 | | /* for stats */ |
1970 | 0 | stats_uri_auth_drop(curproxy->uri_auth); |
1971 | 0 | stats_uri_auth_take(defproxy->uri_auth); |
1972 | 0 | curproxy->uri_auth = defproxy->uri_auth; |
1973 | | |
1974 | | /* copy default loggers to curproxy */ |
1975 | 0 | list_for_each_entry(tmplogger, &defproxy->loggers, list) { |
1976 | 0 | struct logger *node = dup_logger(tmplogger); |
1977 | |
|
1978 | 0 | if (!node) { |
1979 | 0 | memprintf(errmsg, "proxy '%s': out of memory", curproxy->id); |
1980 | 0 | return 1; |
1981 | 0 | } |
1982 | 0 | LIST_APPEND(&curproxy->loggers, &node->list); |
1983 | 0 | } |
1984 | | |
1985 | 0 | lf_expr_dup(&defproxy->format_unique_id, &curproxy->format_unique_id); |
1986 | |
|
1987 | 0 | chunk_dup(&curproxy->log_tag, &defproxy->log_tag); |
1988 | | |
1989 | | /* copy default header unique id */ |
1990 | 0 | if (isttest(defproxy->header_unique_id)) { |
1991 | 0 | const struct ist copy = istdup(defproxy->header_unique_id); |
1992 | |
|
1993 | 0 | if (!isttest(copy)) { |
1994 | 0 | memprintf(errmsg, "proxy '%s': out of memory for unique-id-header", curproxy->id); |
1995 | 0 | return 1; |
1996 | 0 | } |
1997 | 0 | curproxy->header_unique_id = copy; |
1998 | 0 | } |
1999 | | |
2000 | | /* default compression options */ |
2001 | 0 | if (defproxy->comp != NULL) { |
2002 | 0 | curproxy->comp = calloc(1, sizeof(*curproxy->comp)); |
2003 | 0 | if (!curproxy->comp) { |
2004 | 0 | memprintf(errmsg, "proxy '%s': out of memory for default compression options", curproxy->id); |
2005 | 0 | return 1; |
2006 | 0 | } |
2007 | 0 | curproxy->comp->algos_res = defproxy->comp->algos_res; |
2008 | 0 | curproxy->comp->algo_req = defproxy->comp->algo_req; |
2009 | 0 | curproxy->comp->types_res = defproxy->comp->types_res; |
2010 | 0 | curproxy->comp->types_req = defproxy->comp->types_req; |
2011 | 0 | curproxy->comp->minsize_res = defproxy->comp->minsize_res; |
2012 | 0 | curproxy->comp->minsize_req = defproxy->comp->minsize_req; |
2013 | 0 | curproxy->comp->flags = defproxy->comp->flags; |
2014 | 0 | } |
2015 | | |
2016 | 0 | if (defproxy->check_path) |
2017 | 0 | curproxy->check_path = strdup(defproxy->check_path); |
2018 | 0 | if (defproxy->check_command) |
2019 | 0 | curproxy->check_command = strdup(defproxy->check_command); |
2020 | |
|
2021 | 0 | BUG_ON(curproxy->email_alert.flags & PR_EMAIL_ALERT_RESOLVED); |
2022 | 0 | if (defproxy->email_alert.mailers.name) |
2023 | 0 | curproxy->email_alert.mailers.name = strdup(defproxy->email_alert.mailers.name); |
2024 | 0 | if (defproxy->email_alert.from) |
2025 | 0 | curproxy->email_alert.from = strdup(defproxy->email_alert.from); |
2026 | 0 | if (defproxy->email_alert.to) |
2027 | 0 | curproxy->email_alert.to = strdup(defproxy->email_alert.to); |
2028 | 0 | if (defproxy->email_alert.myhostname) |
2029 | 0 | curproxy->email_alert.myhostname = strdup(defproxy->email_alert.myhostname); |
2030 | 0 | curproxy->email_alert.level = defproxy->email_alert.level; |
2031 | 0 | curproxy->email_alert.flags = defproxy->email_alert.flags; |
2032 | | |
2033 | | /* defproxy is const pointer, so we need to typecast log_steps to |
2034 | | * drop the const in order to use EB tree API, please note however |
2035 | | * that the operations performed below should theoretically be read-only |
2036 | | */ |
2037 | 0 | node = eb32_first((struct eb_root *)&defproxy->conf.log_steps); |
2038 | 0 | while (node) { |
2039 | 0 | struct eb32_node *new_node; |
2040 | |
|
2041 | 0 | new_node = malloc(sizeof(*new_node)); |
2042 | 0 | if (!new_node) { |
2043 | 0 | memprintf(errmsg, "proxy '%s': out of memory for log_steps option", curproxy->id); |
2044 | 0 | return 1; |
2045 | 0 | } |
2046 | | |
2047 | 0 | new_node->key = node->key; |
2048 | 0 | eb32_insert(&curproxy->conf.log_steps, new_node); |
2049 | 0 | node = eb32_next(node); |
2050 | 0 | } |
2051 | | |
2052 | 0 | return 0; |
2053 | 0 | } |
2054 | | |
2055 | | /* Allocates a new proxy <name> of type <cap> found at position <file:linenum>, |
2056 | | * preset it from the defaults of <defproxy> and returns it. In case of error, |
2057 | | * an alert is printed and NULL is returned. |
2058 | | */ |
2059 | | struct proxy *parse_new_proxy(const char *name, unsigned int cap, |
2060 | | const char *file, int linenum, |
2061 | | const struct proxy *defproxy) |
2062 | 0 | { |
2063 | 0 | struct proxy *curproxy = NULL; |
2064 | 0 | char *errmsg = NULL; |
2065 | |
|
2066 | 0 | if (!(curproxy = alloc_new_proxy(name, cap, &errmsg))) { |
2067 | 0 | ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); |
2068 | 0 | free(errmsg); |
2069 | 0 | return NULL; |
2070 | 0 | } |
2071 | | |
2072 | 0 | if (defproxy) { |
2073 | 0 | if (proxy_defproxy_cpy(curproxy, defproxy, &errmsg)) { |
2074 | 0 | ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); |
2075 | 0 | free(errmsg); |
2076 | |
|
2077 | 0 | ha_free(&curproxy); |
2078 | 0 | return NULL; |
2079 | 0 | } |
2080 | 0 | } |
2081 | | |
2082 | 0 | curproxy->conf.args.file = curproxy->conf.file = copy_file_name(file); |
2083 | 0 | curproxy->conf.args.line = curproxy->conf.line = linenum; |
2084 | |
|
2085 | 0 | return curproxy; |
2086 | 0 | } |
2087 | | |
2088 | | /* to be called under the proxy lock after pausing some listeners. This will |
2089 | | * automatically update the p->flags flag |
2090 | | */ |
2091 | | void proxy_cond_pause(struct proxy *p) |
2092 | 0 | { |
2093 | 0 | if (p->li_ready) |
2094 | 0 | return; |
2095 | 0 | p->flags |= PR_FL_PAUSED; |
2096 | 0 | } |
2097 | | |
2098 | | /* to be called under the proxy lock after resuming some listeners. This will |
2099 | | * automatically update the p->flags flag |
2100 | | */ |
2101 | | void proxy_cond_resume(struct proxy *p) |
2102 | 0 | { |
2103 | 0 | if (!p->li_ready) |
2104 | 0 | return; |
2105 | 0 | p->flags &= ~PR_FL_PAUSED; |
2106 | 0 | } |
2107 | | |
2108 | | /* to be called under the proxy lock after stopping some listeners. This will |
2109 | | * automatically update the p->flags flag after stopping the last one, and |
2110 | | * will emit a log indicating the proxy's condition. The function is idempotent |
2111 | | * so that it will not emit multiple logs; a proxy will be disabled only once. |
2112 | | */ |
2113 | | void proxy_cond_disable(struct proxy *p) |
2114 | 0 | { |
2115 | 0 | long long cum_conn = 0; |
2116 | 0 | long long cum_sess = 0; |
2117 | |
|
2118 | 0 | if (p->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
2119 | 0 | return; |
2120 | | |
2121 | 0 | if (p->li_ready + p->li_paused > 0) |
2122 | 0 | return; |
2123 | | |
2124 | 0 | p->flags |= PR_FL_STOPPED; |
2125 | | |
2126 | | /* Note: syslog proxies use their own loggers so while it's somewhat OK |
2127 | | * to report them being stopped as a warning, we must not spam their log |
2128 | | * servers which are in fact production servers. For other types (CLI, |
2129 | | * peers, etc) we must not report them at all as they're not really on |
2130 | | * the data plane but on the control plane. |
2131 | | */ |
2132 | 0 | if (p->cap & PR_CAP_FE) |
2133 | 0 | cum_conn = COUNTERS_SHARED_TOTAL(p->fe_counters.shared->tg, cum_conn, HA_ATOMIC_LOAD); |
2134 | 0 | if (p->cap & PR_CAP_BE) |
2135 | 0 | cum_sess = COUNTERS_SHARED_TOTAL(p->be_counters.shared->tg, cum_sess, HA_ATOMIC_LOAD); |
2136 | |
|
2137 | 0 | if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP || p->mode == PR_MODE_SYSLOG || p->mode == PR_MODE_SPOP) && !(p->cap & PR_CAP_INT)) |
2138 | 0 | ha_warning("Proxy %s stopped (cumulated conns: FE: %lld, BE: %lld).\n", |
2139 | 0 | p->id, cum_conn, cum_sess); |
2140 | |
|
2141 | 0 | if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP || p->mode == PR_MODE_SPOP) && !(p->cap & PR_CAP_INT)) |
2142 | 0 | send_log(p, LOG_WARNING, "Proxy %s stopped (cumulated conns: FE: %lld, BE: %lld).\n", |
2143 | 0 | p->id, cum_conn, cum_sess); |
2144 | |
|
2145 | 0 | if (p->table && p->table->size && p->table->sync_task) |
2146 | 0 | task_wakeup(p->table->sync_task, TASK_WOKEN_MSG); |
2147 | |
|
2148 | 0 | if (p->task) |
2149 | 0 | task_wakeup(p->task, TASK_WOKEN_MSG); |
2150 | 0 | } |
2151 | | |
2152 | | /* |
2153 | | * This is the proxy management task. It enables proxies when there are enough |
2154 | | * free streams, or stops them when the table is full. It is designed to be |
2155 | | * called as a task which is woken up upon stopping or when rate limiting must |
2156 | | * be enforced. |
2157 | | */ |
2158 | | struct task *manage_proxy(struct task *t, void *context, unsigned int state) |
2159 | 0 | { |
2160 | 0 | struct proxy *p = context; |
2161 | 0 | int next = TICK_ETERNITY; |
2162 | 0 | unsigned int wait; |
2163 | | |
2164 | | /* We should periodically try to enable listeners waiting for a |
2165 | | * global resource here. |
2166 | | */ |
2167 | | |
2168 | | /* If the proxy holds a stick table, we need to purge all unused |
2169 | | * entries. These are all the ones in the table with ref_cnt == 0 |
2170 | | * and all the ones in the pool used to allocate new entries. Any |
2171 | | * entry attached to an existing stream waiting for a store will |
2172 | | * be in neither list. Any entry being dumped will have ref_cnt > 0. |
2173 | | * However we protect tables that are being synced to peers. |
2174 | | */ |
2175 | 0 | if (unlikely(stopping && (p->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) && p->table && p->table->current)) { |
2176 | |
|
2177 | 0 | if (!p->table->refcnt) { |
2178 | | /* !table->refcnt means there |
2179 | | * is no more pending full resync |
2180 | | * to push to a new process and |
2181 | | * we are free to flush the table. |
2182 | | */ |
2183 | 0 | int budget; |
2184 | 0 | int cleaned_up; |
2185 | | |
2186 | | /* We purposely enforce a budget limitation since we don't want |
2187 | | * to spend too much time purging old entries |
2188 | | * |
2189 | | * This is known to cause the watchdog to occasionnaly trigger if |
2190 | | * the table is huge and all entries become available for purge |
2191 | | * at the same time |
2192 | | * |
2193 | | * Moreover, we must also anticipate the pool_gc() call which |
2194 | | * will also be much slower if there is too much work at once |
2195 | | */ |
2196 | 0 | budget = MIN(p->table->current, (1 << 15)); /* max: 32K */ |
2197 | 0 | cleaned_up = stktable_trash_oldest(p->table, budget); |
2198 | 0 | if (cleaned_up) { |
2199 | | /* immediately release freed memory since we are stopping */ |
2200 | 0 | pool_gc(NULL); |
2201 | 0 | if (cleaned_up > (budget / 2)) { |
2202 | | /* most of the budget was used to purge entries, |
2203 | | * it is very likely that there are still trashable |
2204 | | * entries in the table, reschedule a new cleanup |
2205 | | * attempt ASAP |
2206 | | */ |
2207 | 0 | t->expire = TICK_ETERNITY; |
2208 | 0 | task_wakeup(t, TASK_WOKEN_RES); |
2209 | 0 | return t; |
2210 | 0 | } |
2211 | 0 | } |
2212 | 0 | } |
2213 | 0 | if (p->table->current) { |
2214 | | /* some entries still remain but are not yet available |
2215 | | * for cleanup, let's recheck in one second |
2216 | | */ |
2217 | 0 | next = tick_first(next, tick_add(now_ms, 1000)); |
2218 | 0 | } |
2219 | 0 | } |
2220 | | |
2221 | | /* the rest below is just for frontends */ |
2222 | 0 | if (!(p->cap & PR_CAP_FE)) |
2223 | 0 | goto out; |
2224 | | |
2225 | | /* check the various reasons we may find to block the frontend */ |
2226 | 0 | if (unlikely(p->feconn >= p->maxconn)) |
2227 | 0 | goto out; |
2228 | | |
2229 | 0 | if (p->fe_sps_lim && |
2230 | 0 | (wait = COUNTERS_SHARED_TOTAL_ARG2(p->fe_counters.shared->tg, sess_per_sec, next_event_delay, p->fe_sps_lim, 0))) { |
2231 | | |
2232 | | /* we're blocking because a limit was reached on the number of |
2233 | | * requests/s on the frontend. We want to re-check ASAP, which |
2234 | | * means in 1 ms before estimated expiration date, because the |
2235 | | * timer will have settled down. |
2236 | | */ |
2237 | 0 | next = tick_first(next, tick_add(now_ms, wait)); |
2238 | 0 | goto out; |
2239 | 0 | } |
2240 | | |
2241 | | /* The proxy is not limited so we can re-enable any waiting listener */ |
2242 | 0 | dequeue_proxy_listeners(p, 0); |
2243 | 0 | out: |
2244 | 0 | t->expire = next; |
2245 | 0 | task_queue(t); |
2246 | 0 | return t; |
2247 | 0 | } |
2248 | | |
2249 | | |
2250 | | static int proxy_parse_grace(char **args, int section_type, struct proxy *curpx, |
2251 | | const struct proxy *defpx, const char *file, int line, |
2252 | | char **err) |
2253 | 0 | { |
2254 | 0 | const char *res; |
2255 | |
|
2256 | 0 | if (!*args[1]) { |
2257 | 0 | memprintf(err, "'%s' expects <time> as argument.\n", args[0]); |
2258 | 0 | return -1; |
2259 | 0 | } |
2260 | 0 | res = parse_time_err(args[1], &global.grace_delay, TIME_UNIT_MS); |
2261 | 0 | if (res == PARSE_TIME_OVER) { |
2262 | 0 | memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)", |
2263 | 0 | args[1], args[0]); |
2264 | 0 | return -1; |
2265 | 0 | } |
2266 | 0 | else if (res == PARSE_TIME_UNDER) { |
2267 | 0 | memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)", |
2268 | 0 | args[1], args[0]); |
2269 | 0 | return -1; |
2270 | 0 | } |
2271 | 0 | else if (res) { |
2272 | 0 | memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]); |
2273 | 0 | return -1; |
2274 | 0 | } |
2275 | 0 | return 0; |
2276 | 0 | } |
2277 | | |
2278 | | static int proxy_parse_hard_stop_after(char **args, int section_type, struct proxy *curpx, |
2279 | | const struct proxy *defpx, const char *file, int line, |
2280 | | char **err) |
2281 | 0 | { |
2282 | 0 | const char *res; |
2283 | |
|
2284 | 0 | if (!*args[1]) { |
2285 | 0 | memprintf(err, "'%s' expects <time> as argument.\n", args[0]); |
2286 | 0 | return -1; |
2287 | 0 | } |
2288 | 0 | res = parse_time_err(args[1], &global.hard_stop_after, TIME_UNIT_MS); |
2289 | 0 | if (res == PARSE_TIME_OVER) { |
2290 | 0 | memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)", |
2291 | 0 | args[1], args[0]); |
2292 | 0 | return -1; |
2293 | 0 | } |
2294 | 0 | else if (res == PARSE_TIME_UNDER) { |
2295 | 0 | memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)", |
2296 | 0 | args[1], args[0]); |
2297 | 0 | return -1; |
2298 | 0 | } |
2299 | 0 | else if (res) { |
2300 | 0 | memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]); |
2301 | 0 | return -1; |
2302 | 0 | } |
2303 | 0 | return 0; |
2304 | 0 | } |
2305 | | |
2306 | | static int proxy_parse_close_spread_time(char **args, int section_type, struct proxy *curpx, |
2307 | | const struct proxy *defpx, const char *file, int line, |
2308 | | char **err) |
2309 | 0 | { |
2310 | 0 | const char *res; |
2311 | |
|
2312 | 0 | if (!*args[1]) { |
2313 | 0 | memprintf(err, "'%s' expects <time> as argument.\n", args[0]); |
2314 | 0 | return -1; |
2315 | 0 | } |
2316 | | |
2317 | | /* If close-spread-time is set to "infinite", disable the active connection |
2318 | | * closing during soft-stop. |
2319 | | */ |
2320 | 0 | if (strcmp(args[1], "infinite") == 0) { |
2321 | 0 | global.tune.options |= GTUNE_DISABLE_ACTIVE_CLOSE; |
2322 | 0 | global.close_spread_time = TICK_ETERNITY; |
2323 | 0 | return 0; |
2324 | 0 | } |
2325 | | |
2326 | 0 | res = parse_time_err(args[1], &global.close_spread_time, TIME_UNIT_MS); |
2327 | 0 | if (res == PARSE_TIME_OVER) { |
2328 | 0 | memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)", |
2329 | 0 | args[1], args[0]); |
2330 | 0 | return -1; |
2331 | 0 | } |
2332 | 0 | else if (res == PARSE_TIME_UNDER) { |
2333 | 0 | memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)", |
2334 | 0 | args[1], args[0]); |
2335 | 0 | return -1; |
2336 | 0 | } |
2337 | 0 | else if (res) { |
2338 | 0 | memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]); |
2339 | 0 | return -1; |
2340 | 0 | } |
2341 | 0 | global.tune.options &= ~GTUNE_DISABLE_ACTIVE_CLOSE; |
2342 | |
|
2343 | 0 | return 0; |
2344 | 0 | } |
2345 | | |
2346 | | struct task *hard_stop(struct task *t, void *context, unsigned int state) |
2347 | 0 | { |
2348 | 0 | struct proxy *p; |
2349 | 0 | struct stream *s; |
2350 | 0 | int thr; |
2351 | |
|
2352 | 0 | if (killed) { |
2353 | 0 | ha_warning("Some tasks resisted to hard-stop, exiting now.\n"); |
2354 | 0 | send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n"); |
2355 | 0 | killed = 2; |
2356 | 0 | for (thr = 0; thr < global.nbthread; thr++) |
2357 | 0 | if (_HA_ATOMIC_LOAD(&ha_thread_info[thr].tg->threads_enabled) & ha_thread_info[thr].ltid_bit) |
2358 | 0 | wake_thread(thr); |
2359 | 0 | t->expire = TICK_ETERNITY; |
2360 | 0 | return t; |
2361 | 0 | } |
2362 | | |
2363 | 0 | ha_warning("soft-stop running for too long, performing a hard-stop.\n"); |
2364 | 0 | send_log(NULL, LOG_WARNING, "soft-stop running for too long, performing a hard-stop.\n"); |
2365 | 0 | p = proxies_list; |
2366 | 0 | while (p) { |
2367 | 0 | if ((p->cap & PR_CAP_FE) && (p->feconn > 0)) { |
2368 | 0 | ha_warning("Proxy %s hard-stopped (%d remaining conns will be closed).\n", |
2369 | 0 | p->id, p->feconn); |
2370 | 0 | send_log(p, LOG_WARNING, "Proxy %s hard-stopped (%d remaining conns will be closed).\n", |
2371 | 0 | p->id, p->feconn); |
2372 | 0 | } |
2373 | 0 | p = p->next; |
2374 | 0 | } |
2375 | |
|
2376 | 0 | thread_isolate(); |
2377 | |
|
2378 | 0 | for (thr = 0; thr < global.nbthread; thr++) { |
2379 | 0 | list_for_each_entry(s, &ha_thread_ctx[thr].streams, list) { |
2380 | 0 | stream_shutdown(s, SF_ERR_KILLED); |
2381 | 0 | } |
2382 | 0 | } |
2383 | |
|
2384 | 0 | thread_release(); |
2385 | |
|
2386 | 0 | killed = 1; |
2387 | 0 | t->expire = tick_add(now_ms, MS_TO_TICKS(1000)); |
2388 | 0 | return t; |
2389 | 0 | } |
2390 | | |
2391 | | /* perform the soft-stop right now (i.e. unbind listeners) */ |
2392 | | static void do_soft_stop_now() |
2393 | 0 | { |
2394 | 0 | struct proxy *p; |
2395 | 0 | struct task *task; |
2396 | | |
2397 | | /* disable busy polling to avoid cpu eating for the new process */ |
2398 | 0 | global.tune.options &= ~GTUNE_BUSY_POLLING; |
2399 | |
|
2400 | 0 | if (tick_isset(global.close_spread_time)) { |
2401 | 0 | global.close_spread_end = tick_add(now_ms, global.close_spread_time); |
2402 | 0 | } |
2403 | | |
2404 | | /* schedule a hard-stop after a delay if needed */ |
2405 | 0 | if (tick_isset(global.hard_stop_after)) { |
2406 | 0 | task = task_new_anywhere(); |
2407 | 0 | if (task) { |
2408 | 0 | task->process = hard_stop; |
2409 | 0 | task_schedule(task, tick_add(now_ms, global.hard_stop_after)); |
2410 | 0 | } |
2411 | 0 | else { |
2412 | 0 | ha_alert("out of memory trying to allocate the hard-stop task.\n"); |
2413 | 0 | } |
2414 | 0 | } |
2415 | | |
2416 | | /* we isolate so that we have a chance of stopping listeners in other groups */ |
2417 | 0 | thread_isolate(); |
2418 | | |
2419 | | /* stop all stoppable listeners */ |
2420 | 0 | protocol_stop_now(); |
2421 | |
|
2422 | 0 | thread_release(); |
2423 | | |
2424 | | /* Loop on proxies to stop backends */ |
2425 | 0 | p = proxies_list; |
2426 | 0 | while (p) { |
2427 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock); |
2428 | 0 | proxy_cond_disable(p); |
2429 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock); |
2430 | 0 | p = p->next; |
2431 | 0 | } |
2432 | | |
2433 | | /* signal zero is used to broadcast the "stopping" event */ |
2434 | 0 | signal_handler(0); |
2435 | 0 | } |
2436 | | |
2437 | | /* triggered by a soft-stop delayed with `grace` */ |
2438 | | static struct task *grace_expired(struct task *t, void *context, unsigned int state) |
2439 | 0 | { |
2440 | 0 | ha_notice("Grace period expired, proceeding with soft-stop now.\n"); |
2441 | 0 | send_log(NULL, LOG_NOTICE, "Grace period expired, proceeding with soft-stop now.\n"); |
2442 | 0 | do_soft_stop_now(); |
2443 | 0 | task_destroy(t); |
2444 | 0 | return NULL; |
2445 | 0 | } |
2446 | | |
2447 | | /* |
2448 | | * this function disables health-check servers so that the process will quickly be ignored |
2449 | | * by load balancers. |
2450 | | */ |
2451 | | void soft_stop(void) |
2452 | 0 | { |
2453 | 0 | struct task *task; |
2454 | |
|
2455 | 0 | stopping = 1; |
2456 | |
|
2457 | 0 | if (tick_isset(global.grace_delay)) { |
2458 | 0 | task = task_new_anywhere(); |
2459 | 0 | if (task) { |
2460 | 0 | ha_notice("Scheduling a soft-stop in %u ms.\n", global.grace_delay); |
2461 | 0 | send_log(NULL, LOG_WARNING, "Scheduling a soft-stop in %u ms.\n", global.grace_delay); |
2462 | 0 | task->process = grace_expired; |
2463 | 0 | task_schedule(task, tick_add(now_ms, global.grace_delay)); |
2464 | 0 | return; |
2465 | 0 | } |
2466 | 0 | else { |
2467 | 0 | ha_alert("out of memory trying to allocate the stop-stop task, stopping now.\n"); |
2468 | 0 | } |
2469 | 0 | } |
2470 | | |
2471 | | /* no grace (or failure to enforce it): stop now */ |
2472 | 0 | do_soft_stop_now(); |
2473 | 0 | } |
2474 | | |
2475 | | |
2476 | | /* Temporarily disables listening on all of the proxy's listeners. Upon |
2477 | | * success, the proxy enters the PR_PAUSED state. The function returns 0 |
2478 | | * if it fails, or non-zero on success. |
2479 | | * The function takes the proxy's lock so it's safe to |
2480 | | * call from multiple places. |
2481 | | */ |
2482 | | int pause_proxy(struct proxy *p) |
2483 | 0 | { |
2484 | 0 | struct listener *l; |
2485 | |
|
2486 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock); |
2487 | |
|
2488 | 0 | if (!(p->cap & PR_CAP_FE) || (p->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) || !p->li_ready) |
2489 | 0 | goto end; |
2490 | | |
2491 | 0 | list_for_each_entry(l, &p->conf.listeners, by_fe) |
2492 | 0 | suspend_listener(l, 1, 0); |
2493 | |
|
2494 | 0 | if (p->li_ready) { |
2495 | 0 | ha_warning("%s %s failed to enter pause mode.\n", proxy_cap_str(p->cap), p->id); |
2496 | 0 | send_log(p, LOG_WARNING, "%s %s failed to enter pause mode.\n", proxy_cap_str(p->cap), p->id); |
2497 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock); |
2498 | 0 | return 0; |
2499 | 0 | } |
2500 | 0 | end: |
2501 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock); |
2502 | 0 | return 1; |
2503 | 0 | } |
2504 | | |
2505 | | /* |
2506 | | * This function completely stops a proxy and releases its listeners. It has |
2507 | | * to be called when going down in order to release the ports so that another |
2508 | | * process may bind to them. It must also be called on disabled proxies at the |
2509 | | * end of start-up. If all listeners are closed, the proxy is set to the |
2510 | | * PR_STOPPED state. |
2511 | | * The function takes the proxy's lock so it's safe to |
2512 | | * call from multiple places. |
2513 | | */ |
2514 | | void stop_proxy(struct proxy *p) |
2515 | 0 | { |
2516 | 0 | struct listener *l; |
2517 | |
|
2518 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock); |
2519 | |
|
2520 | 0 | list_for_each_entry(l, &p->conf.listeners, by_fe) |
2521 | 0 | stop_listener(l, 1, 0, 0); |
2522 | |
|
2523 | 0 | if (!(p->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) && !p->li_ready) { |
2524 | | /* might be just a backend */ |
2525 | 0 | p->flags |= PR_FL_STOPPED; |
2526 | 0 | } |
2527 | |
|
2528 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock); |
2529 | 0 | } |
2530 | | |
2531 | | /* This function resumes listening on the specified proxy. It scans all of its |
2532 | | * listeners and tries to enable them all. If any of them fails, the proxy is |
2533 | | * put back to the paused state. It returns 1 upon success, or zero if an error |
2534 | | * is encountered. |
2535 | | * The function takes the proxy's lock so it's safe to |
2536 | | * call from multiple places. |
2537 | | */ |
2538 | | int resume_proxy(struct proxy *p) |
2539 | 0 | { |
2540 | 0 | struct listener *l; |
2541 | 0 | int fail; |
2542 | |
|
2543 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock); |
2544 | |
|
2545 | 0 | if ((p->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) || !p->li_paused) |
2546 | 0 | goto end; |
2547 | | |
2548 | 0 | fail = 0; |
2549 | 0 | list_for_each_entry(l, &p->conf.listeners, by_fe) { |
2550 | 0 | if (!resume_listener(l, 1, 0)) { |
2551 | 0 | int port; |
2552 | |
|
2553 | 0 | port = get_host_port(&l->rx.addr); |
2554 | 0 | if (port) { |
2555 | 0 | ha_warning("Port %d busy while trying to enable %s %s.\n", |
2556 | 0 | port, proxy_cap_str(p->cap), p->id); |
2557 | 0 | send_log(p, LOG_WARNING, "Port %d busy while trying to enable %s %s.\n", |
2558 | 0 | port, proxy_cap_str(p->cap), p->id); |
2559 | 0 | } |
2560 | 0 | else { |
2561 | 0 | ha_warning("Bind on socket %d busy while trying to enable %s %s.\n", |
2562 | 0 | l->luid, proxy_cap_str(p->cap), p->id); |
2563 | 0 | send_log(p, LOG_WARNING, "Bind on socket %d busy while trying to enable %s %s.\n", |
2564 | 0 | l->luid, proxy_cap_str(p->cap), p->id); |
2565 | 0 | } |
2566 | | |
2567 | | /* Another port might have been enabled. Let's stop everything. */ |
2568 | 0 | fail = 1; |
2569 | 0 | break; |
2570 | 0 | } |
2571 | 0 | } |
2572 | |
|
2573 | 0 | if (fail) { |
2574 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock); |
2575 | | /* pause_proxy will take PROXY_LOCK */ |
2576 | 0 | pause_proxy(p); |
2577 | 0 | return 0; |
2578 | 0 | } |
2579 | 0 | end: |
2580 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock); |
2581 | 0 | return 1; |
2582 | 0 | } |
2583 | | |
2584 | | /* Set current stream's backend to <be>. Nothing is done if the |
2585 | | * stream already had a backend assigned, which is indicated by |
2586 | | * s->flags & SF_BE_ASSIGNED. |
2587 | | * All flags, stats and counters which need be updated are updated. |
2588 | | * Returns 1 if done, 0 in case of internal error, eg: lack of resource. |
2589 | | */ |
2590 | | int stream_set_backend(struct stream *s, struct proxy *be) |
2591 | 0 | { |
2592 | 0 | unsigned int req_ana; |
2593 | |
|
2594 | 0 | if (s->flags & SF_BE_ASSIGNED) |
2595 | 0 | return 1; |
2596 | | |
2597 | 0 | if (flt_set_stream_backend(s, be) < 0) |
2598 | 0 | return 0; |
2599 | | |
2600 | 0 | s->be = be; |
2601 | 0 | HA_ATOMIC_UPDATE_MAX(&be->be_counters.conn_max, |
2602 | 0 | HA_ATOMIC_ADD_FETCH(&be->beconn, 1)); |
2603 | 0 | proxy_inc_be_ctr(be); |
2604 | | |
2605 | | /* assign new parameters to the stream from the new backend */ |
2606 | 0 | s->scb->flags &= ~SC_FL_INDEP_STR; |
2607 | 0 | if (be->options2 & PR_O2_INDEPSTR) |
2608 | 0 | s->scb->flags |= SC_FL_INDEP_STR; |
2609 | | |
2610 | | /* We want to enable the backend-specific analysers except those which |
2611 | | * were already run as part of the frontend/listener. Note that it would |
2612 | | * be more reliable to store the list of analysers that have been run, |
2613 | | * but what we do here is OK for now. |
2614 | | */ |
2615 | 0 | req_ana = be->be_req_ana; |
2616 | 0 | if (!(strm_fe(s)->options & PR_O_WREQ_BODY) && be->options & PR_O_WREQ_BODY) { |
2617 | | /* The backend request to parse a request body while it was not |
2618 | | * performed on the frontend, so add the corresponding analyser |
2619 | | */ |
2620 | 0 | req_ana |= AN_REQ_HTTP_BODY; |
2621 | 0 | } |
2622 | 0 | if (IS_HTX_STRM(s) && strm_fe(s)->mode != PR_MODE_HTTP) { |
2623 | | /* The stream was already upgraded to HTTP, so remove analysers |
2624 | | * set during the upgrade |
2625 | | */ |
2626 | 0 | req_ana &= ~(AN_REQ_WAIT_HTTP|AN_REQ_HTTP_PROCESS_FE); |
2627 | 0 | } |
2628 | 0 | s->req.analysers |= req_ana & ~(strm_li(s) ? strm_li(s)->bind_conf->analysers : 0); |
2629 | |
|
2630 | 0 | if (!IS_HTX_STRM(s) && be->mode == PR_MODE_HTTP) { |
2631 | | /* If we chain a TCP frontend to an HTX backend, we must upgrade |
2632 | | * the client mux */ |
2633 | 0 | if (!stream_set_http_mode(s, NULL)) |
2634 | 0 | return 0; |
2635 | 0 | } |
2636 | 0 | else if (IS_HTX_STRM(s) && be->mode != PR_MODE_HTTP) { |
2637 | | /* If a TCP backend is assgiend to an HTX stream, return an |
2638 | | * error. It may happens for a new stream on a previously |
2639 | | * upgraded connections. */ |
2640 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2641 | 0 | s->flags |= SF_ERR_INTERNAL; |
2642 | 0 | return 0; |
2643 | 0 | } |
2644 | 0 | else { |
2645 | | /* If the target backend requires HTTP processing, we have to allocate |
2646 | | * the HTTP transaction if we did not have one. |
2647 | | */ |
2648 | 0 | if (unlikely(!s->txn && be->http_needed && !http_create_txn(s))) |
2649 | 0 | return 0; |
2650 | 0 | } |
2651 | | |
2652 | 0 | s->flags |= SF_BE_ASSIGNED; |
2653 | 0 | if (be->options2 & PR_O2_NODELAY) { |
2654 | 0 | s->scf->flags |= SC_FL_SND_NEVERWAIT; |
2655 | 0 | s->scb->flags |= SC_FL_SND_NEVERWAIT; |
2656 | 0 | } |
2657 | |
|
2658 | 0 | return 1; |
2659 | 0 | } |
2660 | | |
2661 | | /* Capture a bad request or response and archive it in the proxy's structure. |
2662 | | * It is relatively protocol-agnostic so it requires that a number of elements |
2663 | | * are passed : |
2664 | | * - <proxy> is the proxy where the error was detected and where the snapshot |
2665 | | * needs to be stored |
2666 | | * - <is_back> indicates that the error happened when receiving the response |
2667 | | * - <other_end> is a pointer to the proxy on the other side when known |
2668 | | * - <target> is the target of the connection, usually a server or a proxy |
2669 | | * - <sess> is the session which experienced the error |
2670 | | * - <ctx> may be NULL or should contain any info relevant to the protocol |
2671 | | * - <buf> is the buffer containing the offending data |
2672 | | * - <buf_ofs> is the position of this buffer's input data in the input |
2673 | | * stream, starting at zero. It may be passed as zero if unknown. |
2674 | | * - <buf_out> is the portion of <buf->data> which was already forwarded and |
2675 | | * which precedes the buffer's input. The buffer's input starts at |
2676 | | * buf->head + buf_out. |
2677 | | * - <err_pos> is the pointer to the faulty byte in the buffer's input. |
2678 | | * - <show> is the callback to use to display <ctx>. It may be NULL. |
2679 | | */ |
2680 | | void proxy_capture_error(struct proxy *proxy, int is_back, |
2681 | | struct proxy *other_end, enum obj_type *target, |
2682 | | const struct session *sess, |
2683 | | const struct buffer *buf, long buf_ofs, |
2684 | | unsigned int buf_out, unsigned int err_pos, |
2685 | | const union error_snapshot_ctx *ctx, |
2686 | | void (*show)(struct buffer *, const struct error_snapshot *)) |
2687 | 0 | { |
2688 | 0 | struct error_snapshot *es; |
2689 | 0 | unsigned int buf_len; |
2690 | 0 | int len1, len2; |
2691 | 0 | unsigned int ev_id; |
2692 | |
|
2693 | 0 | ev_id = HA_ATOMIC_FETCH_ADD(&error_snapshot_id, 1); |
2694 | |
|
2695 | 0 | buf_len = b_data(buf) - buf_out; |
2696 | |
|
2697 | 0 | es = malloc(sizeof(*es) + buf_len); |
2698 | 0 | if (!es) |
2699 | 0 | return; |
2700 | | |
2701 | 0 | es->buf_len = buf_len; |
2702 | 0 | es->ev_id = ev_id; |
2703 | |
|
2704 | 0 | len1 = b_size(buf) - b_peek_ofs(buf, buf_out); |
2705 | 0 | if (len1 > buf_len) |
2706 | 0 | len1 = buf_len; |
2707 | |
|
2708 | 0 | if (len1) { |
2709 | 0 | memcpy(es->buf, b_peek(buf, buf_out), len1); |
2710 | 0 | len2 = buf_len - len1; |
2711 | 0 | if (len2) |
2712 | 0 | memcpy(es->buf + len1, b_orig(buf), len2); |
2713 | 0 | } |
2714 | |
|
2715 | 0 | es->buf_err = err_pos; |
2716 | 0 | es->when = date; // user-visible date |
2717 | 0 | es->srv = objt_server(target); |
2718 | 0 | es->oe = other_end; |
2719 | 0 | if (sess && objt_conn(sess->origin) && conn_get_src(__objt_conn(sess->origin))) |
2720 | 0 | es->src = *__objt_conn(sess->origin)->src; |
2721 | 0 | else |
2722 | 0 | memset(&es->src, 0, sizeof(es->src)); |
2723 | |
|
2724 | 0 | es->buf_wrap = b_wrap(buf) - b_peek(buf, buf_out); |
2725 | 0 | es->buf_out = buf_out; |
2726 | 0 | es->buf_ofs = buf_ofs; |
2727 | | |
2728 | | /* be sure to indicate the offset of the first IN byte */ |
2729 | 0 | if (es->buf_ofs >= es->buf_len) |
2730 | 0 | es->buf_ofs -= es->buf_len; |
2731 | 0 | else |
2732 | 0 | es->buf_ofs = 0; |
2733 | | |
2734 | | /* protocol-specific part now */ |
2735 | 0 | if (ctx) |
2736 | 0 | es->ctx = *ctx; |
2737 | 0 | else |
2738 | 0 | memset(&es->ctx, 0, sizeof(es->ctx)); |
2739 | 0 | es->show = show; |
2740 | | |
2741 | | /* note: we still lock since we have to be certain that nobody is |
2742 | | * dumping the output while we free. |
2743 | | */ |
2744 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &proxy->lock); |
2745 | 0 | if (is_back) { |
2746 | 0 | es = HA_ATOMIC_XCHG(&proxy->invalid_rep, es); |
2747 | 0 | } else { |
2748 | 0 | es = HA_ATOMIC_XCHG(&proxy->invalid_req, es); |
2749 | 0 | } |
2750 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &proxy->lock); |
2751 | 0 | ha_free(&es); |
2752 | 0 | } |
2753 | | |
2754 | | /* Configure all proxies which lack a maxconn setting to use the global one by |
2755 | | * default. This avoids the common mistake consisting in setting maxconn only |
2756 | | * in the global section and discovering the hard way that it doesn't propagate |
2757 | | * through the frontends. These values are also propagated through the various |
2758 | | * targeted backends, whose fullconn is finally calculated if not yet set. |
2759 | | */ |
2760 | | void proxy_adjust_all_maxconn() |
2761 | 0 | { |
2762 | 0 | struct proxy *curproxy; |
2763 | 0 | struct switching_rule *swrule1, *swrule2; |
2764 | |
|
2765 | 0 | for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) { |
2766 | 0 | if (curproxy->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
2767 | 0 | continue; |
2768 | | |
2769 | 0 | if (!(curproxy->cap & PR_CAP_FE)) |
2770 | 0 | continue; |
2771 | | |
2772 | 0 | if (!curproxy->maxconn) |
2773 | 0 | curproxy->maxconn = global.maxconn; |
2774 | | |
2775 | | /* update the target backend's fullconn count : default_backend */ |
2776 | 0 | if (curproxy->defbe.be) |
2777 | 0 | curproxy->defbe.be->tot_fe_maxconn += curproxy->maxconn; |
2778 | 0 | else if ((curproxy->cap & PR_CAP_LISTEN) == PR_CAP_LISTEN) |
2779 | 0 | curproxy->tot_fe_maxconn += curproxy->maxconn; |
2780 | |
|
2781 | 0 | list_for_each_entry(swrule1, &curproxy->switching_rules, list) { |
2782 | | /* For each target of switching rules, we update their |
2783 | | * tot_fe_maxconn, except if a previous rule points to |
2784 | | * the same backend or to the default backend. |
2785 | | */ |
2786 | 0 | if (swrule1->be.backend != curproxy->defbe.be) { |
2787 | | /* note: swrule1->be.backend isn't a backend if the rule |
2788 | | * is dynamic, it's an expression instead, so it must not |
2789 | | * be dereferenced as a backend before being certain it is. |
2790 | | */ |
2791 | 0 | list_for_each_entry(swrule2, &curproxy->switching_rules, list) { |
2792 | 0 | if (swrule2 == swrule1) { |
2793 | 0 | if (!swrule1->dynamic) |
2794 | 0 | swrule1->be.backend->tot_fe_maxconn += curproxy->maxconn; |
2795 | 0 | break; |
2796 | 0 | } |
2797 | 0 | else if (!swrule2->dynamic && swrule2->be.backend == swrule1->be.backend) { |
2798 | | /* there are multiple refs of this backend */ |
2799 | 0 | break; |
2800 | 0 | } |
2801 | 0 | } |
2802 | 0 | } |
2803 | 0 | } |
2804 | 0 | } |
2805 | | |
2806 | | /* automatically compute fullconn if not set. We must not do it in the |
2807 | | * loop above because cross-references are not yet fully resolved. |
2808 | | */ |
2809 | 0 | for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) { |
2810 | 0 | if (curproxy->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
2811 | 0 | continue; |
2812 | | |
2813 | | /* If <fullconn> is not set, let's set it to 10% of the sum of |
2814 | | * the possible incoming frontend's maxconns. |
2815 | | */ |
2816 | 0 | if (!curproxy->fullconn && (curproxy->cap & PR_CAP_BE)) { |
2817 | | /* we have the sum of the maxconns in <total>. We only |
2818 | | * keep 10% of that sum to set the default fullconn, with |
2819 | | * a hard minimum of 1 (to avoid a divide by zero). |
2820 | | */ |
2821 | 0 | curproxy->fullconn = (curproxy->tot_fe_maxconn + 9) / 10; |
2822 | 0 | if (!curproxy->fullconn) |
2823 | 0 | curproxy->fullconn = 1; |
2824 | 0 | } |
2825 | 0 | } |
2826 | 0 | } |
2827 | | |
2828 | | /* Config keywords below */ |
2829 | | |
2830 | | static struct cfg_kw_list cfg_kws = {ILH, { |
2831 | | { CFG_GLOBAL, "grace", proxy_parse_grace }, |
2832 | | { CFG_GLOBAL, "hard-stop-after", proxy_parse_hard_stop_after }, |
2833 | | { CFG_GLOBAL, "close-spread-time", proxy_parse_close_spread_time }, |
2834 | | { CFG_LISTEN, "timeout", proxy_parse_timeout }, |
2835 | | { CFG_LISTEN, "clitimeout", proxy_parse_timeout }, /* This keyword actually fails to parse, this line remains for better error messages. */ |
2836 | | { CFG_LISTEN, "contimeout", proxy_parse_timeout }, /* This keyword actually fails to parse, this line remains for better error messages. */ |
2837 | | { CFG_LISTEN, "srvtimeout", proxy_parse_timeout }, /* This keyword actually fails to parse, this line remains for better error messages. */ |
2838 | | { CFG_LISTEN, "rate-limit", proxy_parse_rate_limit }, |
2839 | | { CFG_LISTEN, "max-keep-alive-queue", proxy_parse_max_ka_queue }, |
2840 | | { CFG_LISTEN, "declare", proxy_parse_declare }, |
2841 | | { CFG_LISTEN, "retry-on", proxy_parse_retry_on }, |
2842 | | { CFG_LISTEN, "hash-preserve-affinity", proxy_parse_hash_preserve_affinity }, |
2843 | | #ifdef TCP_KEEPCNT |
2844 | | { CFG_LISTEN, "clitcpka-cnt", proxy_parse_tcpka_cnt }, |
2845 | | { CFG_LISTEN, "srvtcpka-cnt", proxy_parse_tcpka_cnt }, |
2846 | | #endif |
2847 | | #ifdef TCP_KEEPIDLE |
2848 | | { CFG_LISTEN, "clitcpka-idle", proxy_parse_tcpka_idle }, |
2849 | | { CFG_LISTEN, "srvtcpka-idle", proxy_parse_tcpka_idle }, |
2850 | | #endif |
2851 | | #ifdef TCP_KEEPINTVL |
2852 | | { CFG_LISTEN, "clitcpka-intvl", proxy_parse_tcpka_intvl }, |
2853 | | { CFG_LISTEN, "srvtcpka-intvl", proxy_parse_tcpka_intvl }, |
2854 | | #endif |
2855 | | { CFG_LISTEN, "guid", proxy_parse_guid }, |
2856 | | { 0, NULL, NULL }, |
2857 | | }}; |
2858 | | |
2859 | | INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); |
2860 | | |
2861 | | /* Expects to find a frontend named <arg> and returns it, otherwise displays various |
2862 | | * adequate error messages and returns NULL. This function is designed to be used by |
2863 | | * functions requiring a frontend on the CLI. |
2864 | | */ |
2865 | | struct proxy *cli_find_frontend(struct appctx *appctx, const char *arg) |
2866 | 0 | { |
2867 | 0 | struct proxy *px; |
2868 | |
|
2869 | 0 | if (!*arg) { |
2870 | 0 | cli_err(appctx, "A frontend name is expected.\n"); |
2871 | 0 | return NULL; |
2872 | 0 | } |
2873 | | |
2874 | 0 | px = proxy_fe_by_name(arg); |
2875 | 0 | if (!px) { |
2876 | 0 | cli_err(appctx, "No such frontend.\n"); |
2877 | 0 | return NULL; |
2878 | 0 | } |
2879 | 0 | return px; |
2880 | 0 | } |
2881 | | |
2882 | | /* Expects to find a backend named <arg> and returns it, otherwise displays various |
2883 | | * adequate error messages and returns NULL. This function is designed to be used by |
2884 | | * functions requiring a frontend on the CLI. |
2885 | | */ |
2886 | | struct proxy *cli_find_backend(struct appctx *appctx, const char *arg) |
2887 | 0 | { |
2888 | 0 | struct proxy *px; |
2889 | |
|
2890 | 0 | if (!*arg) { |
2891 | 0 | cli_err(appctx, "A backend name is expected.\n"); |
2892 | 0 | return NULL; |
2893 | 0 | } |
2894 | | |
2895 | 0 | px = proxy_be_by_name(arg); |
2896 | 0 | if (!px) { |
2897 | 0 | cli_err(appctx, "No such backend.\n"); |
2898 | 0 | return NULL; |
2899 | 0 | } |
2900 | 0 | return px; |
2901 | 0 | } |
2902 | | |
2903 | | |
2904 | | /* parse a "show servers [state|conn]" CLI line, returns 0 if it wants to start |
2905 | | * the dump or 1 if it stops immediately. If an argument is specified, it will |
2906 | | * reserve a show_srv_ctx context and set the proxy pointer into ->px, its ID |
2907 | | * into ->only_pxid, and ->show_conn to 0 for "state", or 1 for "conn". |
2908 | | */ |
2909 | | static int cli_parse_show_servers(char **args, char *payload, struct appctx *appctx, void *private) |
2910 | 0 | { |
2911 | 0 | struct show_srv_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx)); |
2912 | 0 | struct proxy *px; |
2913 | |
|
2914 | 0 | ctx->show_conn = *args[2] == 'c'; // "conn" vs "state" |
2915 | | |
2916 | | /* check if a backend name has been provided */ |
2917 | 0 | if (*args[3]) { |
2918 | | /* read server state from local file */ |
2919 | 0 | px = proxy_be_by_name(args[3]); |
2920 | |
|
2921 | 0 | if (!px) |
2922 | 0 | return cli_err(appctx, "Can't find backend.\n"); |
2923 | | |
2924 | 0 | ctx->px = px; |
2925 | 0 | ctx->only_pxid = px->uuid; |
2926 | 0 | } |
2927 | 0 | return 0; |
2928 | 0 | } |
2929 | | |
2930 | | /* helper to dump server addr */ |
2931 | | static void dump_server_addr(const struct sockaddr_storage *addr, char *addr_str) |
2932 | 0 | { |
2933 | 0 | addr_str[0] = '\0'; |
2934 | 0 | switch (addr->ss_family) { |
2935 | 0 | case AF_INET: |
2936 | 0 | case AF_INET6: |
2937 | 0 | addr_to_str(addr, addr_str, INET6_ADDRSTRLEN + 1); |
2938 | 0 | break; |
2939 | 0 | default: |
2940 | 0 | memcpy(addr_str, "-\0", 2); |
2941 | 0 | break; |
2942 | 0 | } |
2943 | 0 | } |
2944 | | |
2945 | | /* dumps server state information for all the servers found in backend cli.p0. |
2946 | | * These information are all the parameters which may change during HAProxy runtime. |
2947 | | * By default, we only export to the last known server state file format. These |
2948 | | * information can be used at next startup to recover same level of server |
2949 | | * state. It takes its context from show_srv_ctx, with the proxy pointer from |
2950 | | * ->px, the proxy's id ->only_pxid, the server's pointer from ->sv, and the |
2951 | | * choice of what to dump from ->show_conn. |
2952 | | */ |
2953 | | static int dump_servers_state(struct appctx *appctx) |
2954 | 0 | { |
2955 | 0 | struct show_srv_ctx *ctx = appctx->svcctx; |
2956 | 0 | struct proxy *px = ctx->px; |
2957 | 0 | struct server *srv; |
2958 | 0 | char srv_addr[INET6_ADDRSTRLEN + 1]; |
2959 | 0 | char srv_agent_addr[INET6_ADDRSTRLEN + 1]; |
2960 | 0 | char srv_check_addr[INET6_ADDRSTRLEN + 1]; |
2961 | 0 | time_t srv_time_since_last_change; |
2962 | 0 | int bk_f_forced_id, srv_f_forced_id; |
2963 | 0 | char *srvrecord; |
2964 | |
|
2965 | 0 | if (!ctx->sv) |
2966 | 0 | ctx->sv = px->srv; |
2967 | |
|
2968 | 0 | for (; ctx->sv != NULL; ctx->sv = srv->next) { |
2969 | 0 | srv = ctx->sv; |
2970 | |
|
2971 | 0 | dump_server_addr(&srv->addr, srv_addr); |
2972 | 0 | dump_server_addr(&srv->check.addr, srv_check_addr); |
2973 | 0 | dump_server_addr(&srv->agent.addr, srv_agent_addr); |
2974 | |
|
2975 | 0 | srv_time_since_last_change = ns_to_sec(now_ns) - COUNTERS_SHARED_LAST(srv->counters.shared->tg, last_change); |
2976 | 0 | bk_f_forced_id = px->options & PR_O_FORCED_ID ? 1 : 0; |
2977 | 0 | srv_f_forced_id = srv->flags & SRV_F_FORCED_ID ? 1 : 0; |
2978 | |
|
2979 | 0 | srvrecord = NULL; |
2980 | 0 | if (srv->srvrq && srv->srvrq->name) |
2981 | 0 | srvrecord = srv->srvrq->name; |
2982 | |
|
2983 | 0 | if (ctx->show_conn == 0) { |
2984 | | /* show servers state */ |
2985 | 0 | chunk_printf(&trash, |
2986 | 0 | "%d %s " |
2987 | 0 | "%d %s %s " |
2988 | 0 | "%d %d %d %d %ld " |
2989 | 0 | "%d %d %d %d %d " |
2990 | 0 | "%d %d %s %u " |
2991 | 0 | "%s %d %d " |
2992 | 0 | "%s %s %d" |
2993 | 0 | "\n", |
2994 | 0 | px->uuid, HA_ANON_CLI(px->id), |
2995 | 0 | srv->puid, HA_ANON_CLI(srv->id), |
2996 | 0 | hash_ipanon(appctx->cli_ctx.anon_key, srv_addr, 0), |
2997 | 0 | srv->cur_state, srv->cur_admin, srv->uweight, srv->iweight, |
2998 | 0 | (long int)srv_time_since_last_change, |
2999 | 0 | srv->check.status, srv->check.result, srv->check.health, |
3000 | 0 | srv->check.state & 0x0F, srv->agent.state & 0x1F, |
3001 | 0 | bk_f_forced_id, srv_f_forced_id, |
3002 | 0 | srv->hostname ? HA_ANON_CLI(srv->hostname) : "-", srv->svc_port, |
3003 | 0 | srvrecord ? srvrecord : "-", srv->use_ssl, srv->check.port, |
3004 | 0 | srv_check_addr, srv_agent_addr, srv->agent.port); |
3005 | 0 | } else { |
3006 | | /* show servers conn */ |
3007 | 0 | int thr; |
3008 | |
|
3009 | 0 | chunk_printf(&trash, |
3010 | 0 | "%s/%s %d/%d %s %u - %u %u %u %u %u %u %d %u", |
3011 | 0 | HA_ANON_CLI(px->id), HA_ANON_CLI(srv->id), |
3012 | 0 | px->uuid, srv->puid, hash_ipanon(appctx->cli_ctx.anon_key, srv_addr, 0), |
3013 | 0 | srv->svc_port, srv->pool_purge_delay, |
3014 | 0 | srv->curr_used_conns, srv->max_used_conns, srv->est_need_conns, |
3015 | 0 | srv->curr_idle_nb, srv->curr_safe_nb, (int)srv->max_idle_conns, srv->curr_idle_conns); |
3016 | |
|
3017 | 0 | for (thr = 0; thr < global.nbthread && srv->curr_idle_thr; thr++) |
3018 | 0 | chunk_appendf(&trash, " %u", srv->curr_idle_thr[thr]); |
3019 | |
|
3020 | 0 | chunk_appendf(&trash, "\n"); |
3021 | 0 | } |
3022 | |
|
3023 | 0 | if (applet_putchk(appctx, &trash) == -1) { |
3024 | 0 | return 0; |
3025 | 0 | } |
3026 | 0 | } |
3027 | 0 | return 1; |
3028 | 0 | } |
3029 | | |
3030 | | /* Parses backend list or simply use backend name provided by the user to return |
3031 | | * states of servers to stdout. It takes its context from show_srv_ctx and dumps |
3032 | | * proxy ->px and stops if ->only_pxid is non-null. |
3033 | | */ |
3034 | | static int cli_io_handler_servers_state(struct appctx *appctx) |
3035 | 0 | { |
3036 | 0 | struct show_srv_ctx *ctx = appctx->svcctx; |
3037 | 0 | struct proxy *curproxy; |
3038 | |
|
3039 | 0 | if (ctx->state == SHOW_SRV_HEAD) { |
3040 | 0 | if (ctx->show_conn == 0) |
3041 | 0 | chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES); |
3042 | 0 | else |
3043 | 0 | chunk_printf(&trash, |
3044 | 0 | "# bkname/svname bkid/svid addr port - purge_delay used_cur used_max need_est unsafe_nb safe_nb idle_lim idle_cur idle_per_thr[%d]\n", |
3045 | 0 | global.nbthread); |
3046 | |
|
3047 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3048 | 0 | return 0; |
3049 | | |
3050 | 0 | ctx->state = SHOW_SRV_LIST; |
3051 | |
|
3052 | 0 | if (!ctx->px) |
3053 | 0 | ctx->px = proxies_list; |
3054 | 0 | } |
3055 | | |
3056 | 0 | for (; ctx->px != NULL; ctx->px = curproxy->next) { |
3057 | 0 | curproxy = ctx->px; |
3058 | | /* servers are only in backends */ |
3059 | 0 | if ((curproxy->cap & PR_CAP_BE) && !(curproxy->cap & PR_CAP_INT)) { |
3060 | 0 | if (!dump_servers_state(appctx)) |
3061 | 0 | return 0; |
3062 | 0 | } |
3063 | | /* only the selected proxy is dumped */ |
3064 | 0 | if (ctx->only_pxid) |
3065 | 0 | break; |
3066 | 0 | } |
3067 | | |
3068 | 0 | return 1; |
3069 | 0 | } |
3070 | | |
3071 | | /* Parses backend list and simply report backend names. It keeps the proxy |
3072 | | * pointer in svcctx since there's nothing else to store there. |
3073 | | */ |
3074 | | static int cli_io_handler_show_backend(struct appctx *appctx) |
3075 | 0 | { |
3076 | 0 | struct proxy *curproxy; |
3077 | |
|
3078 | 0 | chunk_reset(&trash); |
3079 | |
|
3080 | 0 | if (!appctx->svcctx) { |
3081 | 0 | chunk_printf(&trash, "# name\n"); |
3082 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3083 | 0 | return 0; |
3084 | | |
3085 | 0 | appctx->svcctx = proxies_list; |
3086 | 0 | } |
3087 | | |
3088 | 0 | for (; appctx->svcctx != NULL; appctx->svcctx = curproxy->next) { |
3089 | 0 | curproxy = appctx->svcctx; |
3090 | | |
3091 | | /* looking for non-internal backends only */ |
3092 | 0 | if ((curproxy->cap & (PR_CAP_BE|PR_CAP_INT)) != PR_CAP_BE) |
3093 | 0 | continue; |
3094 | | |
3095 | 0 | chunk_appendf(&trash, "%s\n", curproxy->id); |
3096 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3097 | 0 | return 0; |
3098 | 0 | } |
3099 | | |
3100 | 0 | return 1; |
3101 | 0 | } |
3102 | | |
3103 | | /* Parses the "enable dynamic-cookies backend" directive, it always returns 1. |
3104 | | * |
3105 | | * Grabs the proxy lock and each server's lock. |
3106 | | */ |
3107 | | static int cli_parse_enable_dyncookie_backend(char **args, char *payload, struct appctx *appctx, void *private) |
3108 | 0 | { |
3109 | 0 | struct proxy *px; |
3110 | 0 | struct server *s; |
3111 | |
|
3112 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3113 | 0 | return 1; |
3114 | | |
3115 | 0 | px = cli_find_backend(appctx, args[3]); |
3116 | 0 | if (!px) |
3117 | 0 | return 1; |
3118 | | |
3119 | 0 | if (px->mode != PR_MODE_TCP && px->mode != PR_MODE_HTTP) |
3120 | 0 | return cli_err(appctx, "Not available.\n"); |
3121 | | |
3122 | | /* Note: this lock is to make sure this doesn't change while another |
3123 | | * thread is in srv_set_dyncookie(). |
3124 | | */ |
3125 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock); |
3126 | 0 | px->ck_opts |= PR_CK_DYNAMIC; |
3127 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock); |
3128 | |
|
3129 | 0 | for (s = px->srv; s != NULL; s = s->next) { |
3130 | 0 | HA_SPIN_LOCK(SERVER_LOCK, &s->lock); |
3131 | 0 | srv_set_dyncookie(s); |
3132 | 0 | HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock); |
3133 | 0 | } |
3134 | |
|
3135 | 0 | return 1; |
3136 | 0 | } |
3137 | | |
3138 | | /* Parses the "disable dynamic-cookies backend" directive, it always returns 1. |
3139 | | * |
3140 | | * Grabs the proxy lock and each server's lock. |
3141 | | */ |
3142 | | static int cli_parse_disable_dyncookie_backend(char **args, char *payload, struct appctx *appctx, void *private) |
3143 | 0 | { |
3144 | 0 | struct proxy *px; |
3145 | 0 | struct server *s; |
3146 | |
|
3147 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3148 | 0 | return 1; |
3149 | | |
3150 | 0 | px = cli_find_backend(appctx, args[3]); |
3151 | 0 | if (!px) |
3152 | 0 | return 1; |
3153 | | |
3154 | 0 | if (px->mode != PR_MODE_TCP && px->mode != PR_MODE_HTTP) |
3155 | 0 | return cli_err(appctx, "Not available.\n"); |
3156 | | |
3157 | | /* Note: this lock is to make sure this doesn't change while another |
3158 | | * thread is in srv_set_dyncookie(). |
3159 | | */ |
3160 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock); |
3161 | 0 | px->ck_opts &= ~PR_CK_DYNAMIC; |
3162 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock); |
3163 | |
|
3164 | 0 | for (s = px->srv; s != NULL; s = s->next) { |
3165 | 0 | HA_SPIN_LOCK(SERVER_LOCK, &s->lock); |
3166 | 0 | if (!(s->flags & SRV_F_COOKIESET)) |
3167 | 0 | ha_free(&s->cookie); |
3168 | 0 | HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock); |
3169 | 0 | } |
3170 | |
|
3171 | 0 | return 1; |
3172 | 0 | } |
3173 | | |
3174 | | /* Parses the "set dynamic-cookie-key backend" directive, it always returns 1. |
3175 | | * |
3176 | | * Grabs the proxy lock and each server's lock. |
3177 | | */ |
3178 | | static int cli_parse_set_dyncookie_key_backend(char **args, char *payload, struct appctx *appctx, void *private) |
3179 | 0 | { |
3180 | 0 | struct proxy *px; |
3181 | 0 | struct server *s; |
3182 | 0 | char *newkey; |
3183 | |
|
3184 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3185 | 0 | return 1; |
3186 | | |
3187 | 0 | px = cli_find_backend(appctx, args[3]); |
3188 | 0 | if (!px) |
3189 | 0 | return 1; |
3190 | | |
3191 | 0 | if (px->mode != PR_MODE_TCP && px->mode != PR_MODE_HTTP) |
3192 | 0 | return cli_err(appctx, "Not available.\n"); |
3193 | | |
3194 | 0 | if (!*args[4]) |
3195 | 0 | return cli_err(appctx, "String value expected.\n"); |
3196 | | |
3197 | 0 | newkey = strdup(args[4]); |
3198 | 0 | if (!newkey) |
3199 | 0 | return cli_err(appctx, "Failed to allocate memory.\n"); |
3200 | | |
3201 | | /* Note: this lock is to make sure this doesn't change while another |
3202 | | * thread is in srv_set_dyncookie(). |
3203 | | */ |
3204 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock); |
3205 | 0 | free(px->dyncookie_key); |
3206 | 0 | px->dyncookie_key = newkey; |
3207 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock); |
3208 | |
|
3209 | 0 | for (s = px->srv; s != NULL; s = s->next) { |
3210 | 0 | HA_SPIN_LOCK(SERVER_LOCK, &s->lock); |
3211 | 0 | srv_set_dyncookie(s); |
3212 | 0 | HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock); |
3213 | 0 | } |
3214 | |
|
3215 | 0 | return 1; |
3216 | 0 | } |
3217 | | |
3218 | | /* Parses the "set maxconn frontend" directive, it always returns 1. |
3219 | | * |
3220 | | * Grabs the proxy lock. |
3221 | | */ |
3222 | | static int cli_parse_set_maxconn_frontend(char **args, char *payload, struct appctx *appctx, void *private) |
3223 | 0 | { |
3224 | 0 | struct proxy *px; |
3225 | 0 | struct listener *l; |
3226 | 0 | int v; |
3227 | |
|
3228 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3229 | 0 | return 1; |
3230 | | |
3231 | 0 | px = cli_find_frontend(appctx, args[3]); |
3232 | 0 | if (!px) |
3233 | 0 | return 1; |
3234 | | |
3235 | 0 | if (!*args[4]) |
3236 | 0 | return cli_err(appctx, "Integer value expected.\n"); |
3237 | | |
3238 | 0 | v = atoi(args[4]); |
3239 | 0 | if (v < 0) |
3240 | 0 | return cli_err(appctx, "Value out of range.\n"); |
3241 | | |
3242 | | /* OK, the value is fine, so we assign it to the proxy and to all of |
3243 | | * its listeners. The blocked ones will be dequeued. |
3244 | | */ |
3245 | 0 | HA_RWLOCK_WRLOCK(PROXY_LOCK, &px->lock); |
3246 | |
|
3247 | 0 | px->maxconn = v; |
3248 | 0 | list_for_each_entry(l, &px->conf.listeners, by_fe) { |
3249 | 0 | if (l->state == LI_FULL) |
3250 | 0 | relax_listener(l, 1, 0); |
3251 | 0 | } |
3252 | |
|
3253 | 0 | if (px->maxconn > px->feconn) |
3254 | 0 | dequeue_proxy_listeners(px, 1); |
3255 | |
|
3256 | 0 | HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock); |
3257 | |
|
3258 | 0 | return 1; |
3259 | 0 | } |
3260 | | |
3261 | | /* Parses the "shutdown frontend" directive, it always returns 1. |
3262 | | * |
3263 | | * Grabs the proxy lock. |
3264 | | */ |
3265 | | static int cli_parse_shutdown_frontend(char **args, char *payload, struct appctx *appctx, void *private) |
3266 | 0 | { |
3267 | 0 | struct proxy *px; |
3268 | |
|
3269 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3270 | 0 | return 1; |
3271 | | |
3272 | 0 | px = cli_find_frontend(appctx, args[2]); |
3273 | 0 | if (!px) |
3274 | 0 | return 1; |
3275 | | |
3276 | 0 | if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
3277 | 0 | return cli_msg(appctx, LOG_NOTICE, "Frontend was already shut down.\n"); |
3278 | | |
3279 | 0 | stop_proxy(px); |
3280 | 0 | return 1; |
3281 | 0 | } |
3282 | | |
3283 | | /* Parses the "disable frontend" directive, it always returns 1. |
3284 | | * |
3285 | | * Grabs the proxy lock. |
3286 | | */ |
3287 | | static int cli_parse_disable_frontend(char **args, char *payload, struct appctx *appctx, void *private) |
3288 | 0 | { |
3289 | 0 | struct proxy *px; |
3290 | 0 | int ret; |
3291 | |
|
3292 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3293 | 0 | return 1; |
3294 | | |
3295 | 0 | px = cli_find_frontend(appctx, args[2]); |
3296 | 0 | if (!px) |
3297 | 0 | return 1; |
3298 | | |
3299 | 0 | if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
3300 | 0 | return cli_msg(appctx, LOG_NOTICE, "Frontend was previously shut down, cannot disable.\n"); |
3301 | | |
3302 | 0 | if (!px->li_ready) |
3303 | 0 | return cli_msg(appctx, LOG_NOTICE, "All sockets are already disabled.\n"); |
3304 | | |
3305 | | /* pause_proxy will take PROXY_LOCK */ |
3306 | 0 | ret = pause_proxy(px); |
3307 | |
|
3308 | 0 | if (!ret) |
3309 | 0 | return cli_err(appctx, "Failed to pause frontend, check logs for precise cause.\n"); |
3310 | | |
3311 | 0 | return 1; |
3312 | 0 | } |
3313 | | |
3314 | | /* Parses the "enable frontend" directive, it always returns 1. |
3315 | | * |
3316 | | * Grabs the proxy lock. |
3317 | | */ |
3318 | | static int cli_parse_enable_frontend(char **args, char *payload, struct appctx *appctx, void *private) |
3319 | 0 | { |
3320 | 0 | struct proxy *px; |
3321 | 0 | int ret; |
3322 | |
|
3323 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) |
3324 | 0 | return 1; |
3325 | | |
3326 | 0 | px = cli_find_frontend(appctx, args[2]); |
3327 | 0 | if (!px) |
3328 | 0 | return 1; |
3329 | | |
3330 | 0 | if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
3331 | 0 | return cli_err(appctx, "Frontend was previously shut down, cannot enable.\n"); |
3332 | | |
3333 | 0 | if (px->li_ready == px->li_all) |
3334 | 0 | return cli_msg(appctx, LOG_NOTICE, "All sockets are already enabled.\n"); |
3335 | | |
3336 | | /* resume_proxy will take PROXY_LOCK */ |
3337 | 0 | ret = resume_proxy(px); |
3338 | |
|
3339 | 0 | if (!ret) |
3340 | 0 | return cli_err(appctx, "Failed to resume frontend, check logs for precise cause (port conflict?).\n"); |
3341 | 0 | return 1; |
3342 | 0 | } |
3343 | | |
3344 | | /* appctx context used during "show errors" */ |
3345 | | struct show_errors_ctx { |
3346 | | struct proxy *px; /* current proxy being dumped, NULL = not started yet. */ |
3347 | | unsigned int flag; /* bit0: buffer being dumped, 0 = req, 1 = resp ; bit1=skip req ; bit2=skip resp. */ |
3348 | | unsigned int ev_id; /* event ID of error being dumped */ |
3349 | | int iid; /* if >= 0, ID of the proxy to filter on */ |
3350 | | int ptr; /* <0: headers, >=0 : text pointer to restart from */ |
3351 | | int bol; /* pointer to beginning of current line */ |
3352 | | }; |
3353 | | |
3354 | | /* "show errors" handler for the CLI. Returns 0 if wants to continue, 1 to stop |
3355 | | * now. |
3356 | | */ |
3357 | | static int cli_parse_show_errors(char **args, char *payload, struct appctx *appctx, void *private) |
3358 | 0 | { |
3359 | 0 | struct show_errors_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx)); |
3360 | |
|
3361 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_OPER)) |
3362 | 0 | return 1; |
3363 | | |
3364 | 0 | if (*args[2]) { |
3365 | 0 | struct proxy *px; |
3366 | |
|
3367 | 0 | px = proxy_find_by_name(args[2], 0, 0); |
3368 | 0 | if (px) |
3369 | 0 | ctx->iid = px->uuid; |
3370 | 0 | else |
3371 | 0 | ctx->iid = atoi(args[2]); |
3372 | |
|
3373 | 0 | if (!ctx->iid) |
3374 | 0 | return cli_err(appctx, "No such proxy.\n"); |
3375 | 0 | } |
3376 | 0 | else |
3377 | 0 | ctx->iid = -1; // dump all proxies |
3378 | | |
3379 | 0 | ctx->flag = 0; |
3380 | 0 | if (strcmp(args[3], "request") == 0) |
3381 | 0 | ctx->flag |= 4; // ignore response |
3382 | 0 | else if (strcmp(args[3], "response") == 0) |
3383 | 0 | ctx->flag |= 2; // ignore request |
3384 | 0 | ctx->px = NULL; |
3385 | 0 | return 0; |
3386 | 0 | } |
3387 | | |
3388 | | /* This function dumps all captured errors onto the stream connector's |
3389 | | * read buffer. It returns 0 if the output buffer is full and it needs |
3390 | | * to be called again, otherwise non-zero. |
3391 | | */ |
3392 | | static int cli_io_handler_show_errors(struct appctx *appctx) |
3393 | 0 | { |
3394 | 0 | struct show_errors_ctx *ctx = appctx->svcctx; |
3395 | 0 | extern const char *monthname[12]; |
3396 | |
|
3397 | 0 | chunk_reset(&trash); |
3398 | |
|
3399 | 0 | if (!ctx->px) { |
3400 | | /* the function had not been called yet, let's prepare the |
3401 | | * buffer for a response. |
3402 | | */ |
3403 | 0 | struct tm tm; |
3404 | |
|
3405 | 0 | get_localtime(date.tv_sec, &tm); |
3406 | 0 | chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n", |
3407 | 0 | tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, |
3408 | 0 | tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000), |
3409 | 0 | error_snapshot_id); |
3410 | |
|
3411 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3412 | 0 | goto cant_send; |
3413 | | |
3414 | 0 | ctx->px = proxies_list; |
3415 | 0 | ctx->bol = 0; |
3416 | 0 | ctx->ptr = -1; |
3417 | 0 | } |
3418 | | |
3419 | | /* we have two inner loops here, one for the proxy, the other one for |
3420 | | * the buffer. |
3421 | | */ |
3422 | 0 | while (ctx->px) { |
3423 | 0 | struct error_snapshot *es; |
3424 | |
|
3425 | 0 | HA_RWLOCK_RDLOCK(PROXY_LOCK, &ctx->px->lock); |
3426 | |
|
3427 | 0 | if ((ctx->flag & 1) == 0) { |
3428 | 0 | es = ctx->px->invalid_req; |
3429 | 0 | if (ctx->flag & 2) // skip req |
3430 | 0 | goto next; |
3431 | 0 | } |
3432 | 0 | else { |
3433 | 0 | es = ctx->px->invalid_rep; |
3434 | 0 | if (ctx->flag & 4) // skip resp |
3435 | 0 | goto next; |
3436 | 0 | } |
3437 | | |
3438 | 0 | if (!es) |
3439 | 0 | goto next; |
3440 | | |
3441 | 0 | if (ctx->iid >= 0 && |
3442 | 0 | ctx->px->uuid != ctx->iid && |
3443 | 0 | (!es->oe || es->oe->uuid != ctx->iid)) |
3444 | 0 | goto next; |
3445 | | |
3446 | 0 | if (ctx->ptr < 0) { |
3447 | | /* just print headers now */ |
3448 | |
|
3449 | 0 | char pn[INET6_ADDRSTRLEN]; |
3450 | 0 | struct tm tm; |
3451 | 0 | int port; |
3452 | |
|
3453 | 0 | get_localtime(es->when.tv_sec, &tm); |
3454 | 0 | chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]", |
3455 | 0 | tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, |
3456 | 0 | tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000)); |
3457 | |
|
3458 | 0 | switch (addr_to_str(&es->src, pn, sizeof(pn))) { |
3459 | 0 | case AF_INET: |
3460 | 0 | case AF_INET6: |
3461 | 0 | port = get_host_port(&es->src); |
3462 | 0 | break; |
3463 | 0 | default: |
3464 | 0 | port = 0; |
3465 | 0 | } |
3466 | | |
3467 | 0 | switch (ctx->flag & 1) { |
3468 | 0 | case 0: |
3469 | 0 | chunk_appendf(&trash, |
3470 | 0 | " frontend %s (#%d): invalid request\n" |
3471 | 0 | " backend %s (#%d)", |
3472 | 0 | ctx->px->id, ctx->px->uuid, |
3473 | 0 | (es->oe && es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>", |
3474 | 0 | (es->oe && es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1); |
3475 | 0 | break; |
3476 | 0 | case 1: |
3477 | 0 | chunk_appendf(&trash, |
3478 | 0 | " backend %s (#%d): invalid response\n" |
3479 | 0 | " frontend %s (#%d)", |
3480 | 0 | ctx->px->id, ctx->px->uuid, |
3481 | 0 | es->oe ? es->oe->id : "<NONE>" , es->oe ? es->oe->uuid : -1); |
3482 | 0 | break; |
3483 | 0 | } |
3484 | | |
3485 | 0 | chunk_appendf(&trash, |
3486 | 0 | ", server %s (#%d), event #%u, src %s:%d\n" |
3487 | 0 | " buffer starts at %llu (including %u out), %u free,\n" |
3488 | 0 | " len %u, wraps at %u, error at position %u\n", |
3489 | 0 | es->srv ? es->srv->id : "<NONE>", |
3490 | 0 | es->srv ? es->srv->puid : -1, |
3491 | 0 | es->ev_id, pn, port, |
3492 | 0 | es->buf_ofs, es->buf_out, |
3493 | 0 | global.tune.bufsize - es->buf_out - es->buf_len, |
3494 | 0 | es->buf_len, es->buf_wrap, es->buf_err); |
3495 | |
|
3496 | 0 | if (es->show) |
3497 | 0 | es->show(&trash, es); |
3498 | |
|
3499 | 0 | chunk_appendf(&trash, " \n"); |
3500 | |
|
3501 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3502 | 0 | goto cant_send_unlock; |
3503 | | |
3504 | 0 | ctx->ptr = 0; |
3505 | 0 | ctx->ev_id = es->ev_id; |
3506 | 0 | } |
3507 | | |
3508 | 0 | if (ctx->ev_id != es->ev_id) { |
3509 | | /* the snapshot changed while we were dumping it */ |
3510 | 0 | chunk_appendf(&trash, |
3511 | 0 | " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n"); |
3512 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3513 | 0 | goto cant_send_unlock; |
3514 | | |
3515 | 0 | goto next; |
3516 | 0 | } |
3517 | | |
3518 | | /* OK, ptr >= 0, so we have to dump the current line */ |
3519 | 0 | while (ctx->ptr < es->buf_len && ctx->ptr < global.tune.bufsize) { |
3520 | 0 | int newptr; |
3521 | 0 | int newline; |
3522 | |
|
3523 | 0 | newline = ctx->bol; |
3524 | 0 | newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->buf_len, &newline, ctx->ptr); |
3525 | 0 | if (newptr == ctx->ptr) { |
3526 | 0 | applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL); |
3527 | 0 | goto cant_send_unlock; |
3528 | 0 | } |
3529 | | |
3530 | 0 | if (applet_putchk(appctx, &trash) == -1) |
3531 | 0 | goto cant_send_unlock; |
3532 | | |
3533 | 0 | ctx->ptr = newptr; |
3534 | 0 | ctx->bol = newline; |
3535 | 0 | }; |
3536 | 0 | next: |
3537 | 0 | HA_RWLOCK_RDUNLOCK(PROXY_LOCK, &ctx->px->lock); |
3538 | 0 | ctx->bol = 0; |
3539 | 0 | ctx->ptr = -1; |
3540 | 0 | ctx->flag ^= 1; |
3541 | 0 | if (!(ctx->flag & 1)) |
3542 | 0 | ctx->px = ctx->px->next; |
3543 | 0 | } |
3544 | | |
3545 | | /* dump complete */ |
3546 | 0 | return 1; |
3547 | | |
3548 | 0 | cant_send_unlock: |
3549 | 0 | HA_RWLOCK_RDUNLOCK(PROXY_LOCK, &ctx->px->lock); |
3550 | 0 | cant_send: |
3551 | 0 | return 0; |
3552 | 0 | } |
3553 | | |
3554 | | /* register cli keywords */ |
3555 | | static struct cli_kw_list cli_kws = {{ },{ |
3556 | | { { "disable", "frontend", NULL }, "disable frontend <frontend> : temporarily disable specific frontend", cli_parse_disable_frontend, NULL, NULL }, |
3557 | | { { "enable", "frontend", NULL }, "enable frontend <frontend> : re-enable specific frontend", cli_parse_enable_frontend, NULL, NULL }, |
3558 | | { { "set", "maxconn", "frontend", NULL }, "set maxconn frontend <frontend> <value> : change a frontend's maxconn setting", cli_parse_set_maxconn_frontend, NULL }, |
3559 | | { { "show","servers", "conn", NULL }, "show servers conn [<backend>] : dump server connections status (all or for a single backend)", cli_parse_show_servers, cli_io_handler_servers_state }, |
3560 | | { { "show","servers", "state", NULL }, "show servers state [<backend>] : dump volatile server information (all or for a single backend)", cli_parse_show_servers, cli_io_handler_servers_state }, |
3561 | | { { "show", "backend", NULL }, "show backend : list backends in the current running config", NULL, cli_io_handler_show_backend }, |
3562 | | { { "shutdown", "frontend", NULL }, "shutdown frontend <frontend> : stop a specific frontend", cli_parse_shutdown_frontend, NULL, NULL }, |
3563 | | { { "set", "dynamic-cookie-key", "backend", NULL }, "set dynamic-cookie-key backend <bk> <k> : change a backend secret key for dynamic cookies", cli_parse_set_dyncookie_key_backend, NULL }, |
3564 | | { { "enable", "dynamic-cookie", "backend", NULL }, "enable dynamic-cookie backend <bk> : enable dynamic cookies on a specific backend", cli_parse_enable_dyncookie_backend, NULL }, |
3565 | | { { "disable", "dynamic-cookie", "backend", NULL }, "disable dynamic-cookie backend <bk> : disable dynamic cookies on a specific backend", cli_parse_disable_dyncookie_backend, NULL }, |
3566 | | { { "show", "errors", NULL }, "show errors [<px>] [request|response] : report last request and/or response errors for each proxy", cli_parse_show_errors, cli_io_handler_show_errors, NULL }, |
3567 | | {{},} |
3568 | | }}; |
3569 | | |
3570 | | INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); |
3571 | | |
3572 | | /* |
3573 | | * Local variables: |
3574 | | * c-indent-level: 8 |
3575 | | * c-basic-offset: 8 |
3576 | | * End: |
3577 | | */ |