1 /*
2 * General logging functions.
3 *
4 * Copyright 2000-2008 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 <ctype.h>
14 #include <fcntl.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <syslog.h>
20 #include <time.h>
21 #include <unistd.h>
22 #include <errno.h>
23
24 #include <sys/time.h>
25 #include <sys/uio.h>
26
27 #include <common/config.h>
28 #include <common/compat.h>
29 #include <common/initcall.h>
30 #include <common/standard.h>
31 #include <common/time.h>
32
33 #include <types/cli.h>
34 #include <types/global.h>
35 #include <types/log.h>
36
37 #include <proto/applet.h>
38 #include <proto/cli.h>
39 #include <proto/fd.h>
40 #include <proto/frontend.h>
41 #include <proto/log.h>
42 #include <proto/ring.h>
43 #include <proto/sample.h>
44 #include <proto/sink.h>
45 #include <proto/ssl_sock.h>
46 #include <proto/stream.h>
47 #include <proto/stream_interface.h>
48
49 struct log_fmt {
50 char *name;
51 struct {
52 struct buffer sep1; /* first pid separator */
53 struct buffer sep2; /* second pid separator */
54 } pid;
55 };
56
57 static const struct log_fmt log_formats[LOG_FORMATS] = {
58 [LOG_FORMAT_RFC3164] = {
59 .name = "rfc3164",
60 .pid = {
61 .sep1 = { .area = "[", .data = 1 },
62 .sep2 = { .area = "]: ", .data = 3 }
63 }
64 },
65 [LOG_FORMAT_RFC5424] = {
66 .name = "rfc5424",
67 .pid = {
68 .sep1 = { .area = " ", .data = 1 },
69 .sep2 = { .area = " - ", .data = 3 }
70 }
71 },
72 [LOG_FORMAT_SHORT] = {
73 .name = "short",
74 .pid = {
75 .sep1 = { .area = "", .data = 0 },
76 .sep2 = { .area = " ", .data = 1 },
77 }
78 },
79 [LOG_FORMAT_RAW] = {
80 .name = "raw",
81 .pid = {
82 .sep1 = { .area = "", .data = 0 },
83 .sep2 = { .area = "", .data = 0 },
84 }
85 },
86 };
87
88 /*
89 * This map is used with all the FD_* macros to check whether a particular bit
90 * is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
91 * bytes which should be escaped. When ha_bit_test() returns non-zero, it means
92 * that the byte should be escaped. Be careful to always pass bytes from 0 to
93 * 255 exclusively to the macros.
94 */
95 long rfc5424_escape_map[(256/8) / sizeof(long)];
96 long hdr_encode_map[(256/8) / sizeof(long)];
97 long url_encode_map[(256/8) / sizeof(long)];
98 long http_encode_map[(256/8) / sizeof(long)];
99
100
101 const char *log_facilities[NB_LOG_FACILITIES] = {
102 "kern", "user", "mail", "daemon",
103 "auth", "syslog", "lpr", "news",
104 "uucp", "cron", "auth2", "ftp",
105 "ntp", "audit", "alert", "cron2",
106 "local0", "local1", "local2", "local3",
107 "local4", "local5", "local6", "local7"
108 };
109
110 const char *log_levels[NB_LOG_LEVELS] = {
111 "emerg", "alert", "crit", "err",
112 "warning", "notice", "info", "debug"
113 };
114
115 const char sess_term_cond[16] = "-LcCsSPRIDKUIIII"; /* normal, Local, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal, Down, Killed, Up, -- */
116 const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
117
118
119 /* log_format */
120 struct logformat_type {
121 char *name;
122 int type;
123 int mode;
124 int lw; /* logwait bitsfield */
125 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
126 const char *replace_by; /* new option to use instead of old one */
127 };
128
129 int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
130
131 /* log_format variable names */
132 static const struct logformat_type logformat_keywords[] = {
133 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
134
135 /* please keep these lines sorted ! */
136 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
137 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
138 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
139 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
140 { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
141 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
142 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
143 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
144 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
145 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
146 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
147 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
148 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
149 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
150 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
151 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
152 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
153 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
154 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
155 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
156 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
157 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
158 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
159 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
160 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
161 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
162 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
163 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
164 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
165 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
166 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
167 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
168 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
169 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
170 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
171 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
172 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
173 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
174 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
175 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
176 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
177 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
178 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
179 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
180 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
181 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
182 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
183 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
184 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
185 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
186 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
187 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
188 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
189 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
190 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
191 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
192 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
193 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
194 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
195 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
196 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
197
198 /* The following tags are deprecated and will be removed soon */
199 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
200 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
201 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
202 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
203 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
204 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
205 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
206 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
207 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
208 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
209 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
210 { 0, 0, 0, 0, NULL }
211 };
212
213 char default_http_log_format[] = "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"; // default format
214 char clf_http_log_format[] = "%{+Q}o %{-Q}ci - - [%trg] %r %ST %B \"\" \"\" %cp %ms %ft %b %s %TR %Tw %Tc %Tr %Ta %tsc %ac %fc %bc %sc %rc %sq %bq %CC %CS %hrl %hsl";
215 char default_tcp_log_format[] = "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq";
216 char *log_format = NULL;
217
218 /* Default string used for structured-data part in RFC5424 formatted
219 * syslog messages.
220 */
221 char default_rfc5424_sd_log_format[] = "- ";
222
223 /* total number of dropped logs */
224 unsigned int dropped_logs = 0;
225
226 /* This is a global syslog header, common to all outgoing messages in
227 * RFC3164 format. It begins with time-based part and is updated by
228 * update_log_hdr().
229 */
230 THREAD_LOCAL char *logheader = NULL;
231 THREAD_LOCAL char *logheader_end = NULL;
232
233 /* This is a global syslog header for messages in RFC5424 format. It is
234 * updated by update_log_hdr_rfc5424().
235 */
236 THREAD_LOCAL char *logheader_rfc5424 = NULL;
237 THREAD_LOCAL char *logheader_rfc5424_end = NULL;
238
239 /* This is a global syslog message buffer, common to all outgoing
240 * messages. It contains only the data part.
241 */
242 THREAD_LOCAL char *logline = NULL;
243
244 /* A global syslog message buffer, common to all RFC5424 syslog messages.
245 * Currently, it is used for generating the structured-data part.
246 */
247 THREAD_LOCAL char *logline_rfc5424 = NULL;
248
249 /* A global buffer used to store all startup alerts/warnings. It will then be
250 * retrieve on the CLI. */
251 static struct ring *startup_logs = NULL;
252
253 struct logformat_var_args {
254 char *name;
255 int mask;
256 };
257
258 struct logformat_var_args var_args_list[] = {
259 // global
260 { "M", LOG_OPT_MANDATORY },
261 { "Q", LOG_OPT_QUOTE },
262 { "X", LOG_OPT_HEXA },
263 { "E", LOG_OPT_ESC },
264 { 0, 0 }
265 };
266
267 /* return the name of the directive used in the current proxy for which we're
268 * currently parsing a header, when it is known.
269 */
fmt_directive(const struct proxy * curproxy)270 static inline const char *fmt_directive(const struct proxy *curproxy)
271 {
272 switch (curproxy->conf.args.ctx) {
273 case ARGC_ACL:
274 return "acl";
275 case ARGC_STK:
276 return "stick";
277 case ARGC_TRK:
278 return "track-sc";
279 case ARGC_LOG:
280 return "log-format";
281 case ARGC_LOGSD:
282 return "log-format-sd";
283 case ARGC_HRQ:
284 return "http-request";
285 case ARGC_HRS:
286 return "http-response";
287 case ARGC_UIF:
288 return "unique-id-format";
289 case ARGC_RDR:
290 return "redirect";
291 case ARGC_CAP:
292 return "capture";
293 case ARGC_SRV:
294 return "server";
295 case ARGC_SPOE:
296 return "spoe-message";
297 case ARGC_UBK:
298 return "use_backend";
299 default:
300 return "undefined(please report this bug)"; /* must never happen */
301 }
302 }
303
304 /*
305 * callback used to configure addr source retrieval
306 */
prepare_addrsource(struct logformat_node * node,struct proxy * curproxy)307 int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
308 {
309 curproxy->options2 |= PR_O2_SRC_ADDR;
310
311 return 0;
312 }
313
314
315 /*
316 * Parse args in a logformat_var. Returns 0 in error
317 * case, otherwise, it returns 1.
318 */
parse_logformat_var_args(char * args,struct logformat_node * node,char ** err)319 int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
320 {
321 int i = 0;
322 int end = 0;
323 int flags = 0; // 1 = + 2 = -
324 char *sp = NULL; // start pointer
325
326 if (args == NULL) {
327 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
328 return 0;
329 }
330
331 while (1) {
332 if (*args == '\0')
333 end = 1;
334
335 if (*args == '+') {
336 // add flag
337 sp = args + 1;
338 flags = 1;
339 }
340 if (*args == '-') {
341 // delete flag
342 sp = args + 1;
343 flags = 2;
344 }
345
346 if (*args == '\0' || *args == ',') {
347 *args = '\0';
348 for (i = 0; sp && var_args_list[i].name; i++) {
349 if (strcmp(sp, var_args_list[i].name) == 0) {
350 if (flags == 1) {
351 node->options |= var_args_list[i].mask;
352 break;
353 } else if (flags == 2) {
354 node->options &= ~var_args_list[i].mask;
355 break;
356 }
357 }
358 }
359 sp = NULL;
360 if (end)
361 break;
362 }
363 args++;
364 }
365 return 1;
366 }
367
368 /*
369 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
370 * must pass the args part in the <arg> pointer with its length in <arg_len>,
371 * and varname with its length in <var> and <var_len> respectively. <arg> is
372 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
373 * Returns false in error case and err is filled, otherwise returns true.
374 */
parse_logformat_var(char * arg,int arg_len,char * var,int var_len,struct proxy * curproxy,struct list * list_format,int * defoptions,char ** err)375 int parse_logformat_var(char *arg, int arg_len, char *var, int var_len, struct proxy *curproxy, struct list *list_format, int *defoptions, char **err)
376 {
377 int j;
378 struct logformat_node *node = NULL;
379
380 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
381 if (strlen(logformat_keywords[j].name) == var_len &&
382 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
383 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
384 node = calloc(1, sizeof(*node));
385 if (!node) {
386 memprintf(err, "out of memory error");
387 goto error_free;
388 }
389 node->type = logformat_keywords[j].type;
390 node->options = *defoptions;
391 if (arg_len) {
392 node->arg = my_strndup(arg, arg_len);
393 if (!parse_logformat_var_args(node->arg, node, err))
394 goto error_free;
395 }
396 if (node->type == LOG_FMT_GLOBAL) {
397 *defoptions = node->options;
398 free(node->arg);
399 free(node);
400 } else {
401 if (logformat_keywords[j].config_callback &&
402 logformat_keywords[j].config_callback(node, curproxy) != 0) {
403 goto error_free;
404 }
405 curproxy->to_log |= logformat_keywords[j].lw;
406 LIST_ADDQ(list_format, &node->list);
407 }
408 if (logformat_keywords[j].replace_by)
409 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
410 curproxy->conf.args.file, curproxy->conf.args.line,
411 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
412 return 1;
413 } else {
414 memprintf(err, "format variable '%s' is reserved for HTTP mode",
415 logformat_keywords[j].name);
416 goto error_free;
417 }
418 }
419 }
420
421 j = var[var_len];
422 var[var_len] = 0;
423 memprintf(err, "no such format variable '%s'. If you wanted to emit the '%%' character verbatim, you need to use '%%%%'", var);
424 var[var_len] = j;
425
426 error_free:
427 if (node) {
428 free(node->arg);
429 free(node);
430 }
431 return 0;
432 }
433
434 /*
435 * push to the logformat linked list
436 *
437 * start: start pointer
438 * end: end text pointer
439 * type: string type
440 * list_format: destination list
441 *
442 * LOG_TEXT: copy chars from start to end excluding end.
443 *
444 */
add_to_logformat_list(char * start,char * end,int type,struct list * list_format,char ** err)445 int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
446 {
447 char *str;
448
449 if (type == LF_TEXT) { /* type text */
450 struct logformat_node *node = calloc(1, sizeof(*node));
451 if (!node) {
452 memprintf(err, "out of memory error");
453 return 0;
454 }
455 str = calloc(1, end - start + 1);
456 strncpy(str, start, end - start);
457 str[end - start] = '\0';
458 node->arg = str;
459 node->type = LOG_FMT_TEXT; // type string
460 LIST_ADDQ(list_format, &node->list);
461 } else if (type == LF_SEPARATOR) {
462 struct logformat_node *node = calloc(1, sizeof(*node));
463 if (!node) {
464 memprintf(err, "out of memory error");
465 return 0;
466 }
467 node->type = LOG_FMT_SEPARATOR;
468 LIST_ADDQ(list_format, &node->list);
469 }
470 return 1;
471 }
472
473 /*
474 * Parse the sample fetch expression <text> and add a node to <list_format> upon
475 * success. At the moment, sample converters are not yet supported but fetch arguments
476 * should work. The curpx->conf.args.ctx must be set by the caller.
477 *
478 * In error case, the function returns 0, otherwise it returns 1.
479 */
add_sample_to_logformat_list(char * text,char * arg,int arg_len,struct proxy * curpx,struct list * list_format,int options,int cap,char ** err)480 int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap, char **err)
481 {
482 char *cmd[2];
483 struct sample_expr *expr = NULL;
484 struct logformat_node *node = NULL;
485 int cmd_arg;
486
487 cmd[0] = text;
488 cmd[1] = "";
489 cmd_arg = 0;
490
491 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args);
492 if (!expr) {
493 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
494 goto error_free;
495 }
496
497 node = calloc(1, sizeof(*node));
498 if (!node) {
499 memprintf(err, "out of memory error");
500 goto error_free;
501 }
502 node->type = LOG_FMT_EXPR;
503 node->expr = expr;
504 node->options = options;
505
506 if (arg_len) {
507 node->arg = my_strndup(arg, arg_len);
508 if (!parse_logformat_var_args(node->arg, node, err))
509 goto error_free;
510 }
511 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
512 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
513
514 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
515 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
516
517 if (!(expr->fetch->val & cap)) {
518 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
519 text, sample_src_names(expr->fetch->use));
520 goto error_free;
521 }
522
523 /* check if we need to allocate an http_txn struct for HTTP parsing */
524 /* Note, we may also need to set curpx->to_log with certain fetches */
525 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
526
527 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
528 * needed with some sample fetches (eg: ssl*). We always set it for
529 * now on, but this will leave with sample capabilities soon.
530 */
531 curpx->to_log |= LW_XPRT;
532 curpx->to_log |= LW_REQ;
533 LIST_ADDQ(list_format, &node->list);
534 return 1;
535
536 error_free:
537 release_sample_expr(expr);
538 if (node) {
539 free(node->arg);
540 free(node);
541 }
542 return 0;
543 }
544
545 /*
546 * Parse the log_format string and fill a linked list.
547 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
548 * You can set arguments using { } : %{many arguments}varname.
549 * The curproxy->conf.args.ctx must be set by the caller.
550 *
551 * str: the string to parse
552 * curproxy: the proxy affected
553 * list_format: the destination list
554 * options: LOG_OPT_* to force on every node
555 * cap: all SMP_VAL_* flags supported by the consumer
556 *
557 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
558 */
parse_logformat_string(const char * fmt,struct proxy * curproxy,struct list * list_format,int options,int cap,char ** err)559 int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list *list_format, int options, int cap, char **err)
560 {
561 char *sp, *str, *backfmt; /* start pointer for text parts */
562 char *arg = NULL; /* start pointer for args */
563 char *var = NULL; /* start pointer for vars */
564 int arg_len = 0;
565 int var_len = 0;
566 int cformat; /* current token format */
567 int pformat; /* previous token format */
568 struct logformat_node *tmplf, *back;
569
570 sp = str = backfmt = strdup(fmt);
571 if (!str) {
572 memprintf(err, "out of memory error");
573 return 0;
574 }
575 curproxy->to_log |= LW_INIT;
576
577 /* flush the list first. */
578 list_for_each_entry_safe(tmplf, back, list_format, list) {
579 LIST_DEL(&tmplf->list);
580 release_sample_expr(tmplf->expr);
581 free(tmplf->arg);
582 free(tmplf);
583 }
584
585 for (cformat = LF_INIT; cformat != LF_END; str++) {
586 pformat = cformat;
587
588 if (!*str)
589 cformat = LF_END; // preset it to save all states from doing this
590
591 /* The principle of the two-step state machine below is to first detect a change, and
592 * second have all common paths processed at one place. The common paths are the ones
593 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
594 * We use the common LF_INIT state to dispatch to the different final states.
595 */
596 switch (pformat) {
597 case LF_STARTVAR: // text immediately following a '%'
598 arg = NULL; var = NULL;
599 arg_len = var_len = 0;
600 if (*str == '{') { // optional argument
601 cformat = LF_STARG;
602 arg = str + 1;
603 }
604 else if (*str == '[') {
605 cformat = LF_STEXPR;
606 var = str + 1; // store expr in variable name
607 }
608 else if (isalpha((unsigned char)*str)) { // variable name
609 cformat = LF_VAR;
610 var = str;
611 }
612 else if (*str == '%')
613 cformat = LF_TEXT; // convert this character to a litteral (useful for '%')
614 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
615 /* single '%' followed by blank or digit, send them both */
616 cformat = LF_TEXT;
617 pformat = LF_TEXT; /* finally we include the previous char as well */
618 sp = str - 1; /* send both the '%' and the current char */
619 memprintf(err, "unexpected variable name near '%c' at position %d line : '%s'. Maybe you want to write a single '%%', use the syntax '%%%%'",
620 *str, (int)(str - backfmt), fmt);
621 goto fail;
622
623 }
624 else
625 cformat = LF_INIT; // handle other cases of litterals
626 break;
627
628 case LF_STARG: // text immediately following '%{'
629 if (*str == '}') { // end of arg
630 cformat = LF_EDARG;
631 arg_len = str - arg;
632 *str = 0; // used for reporting errors
633 }
634 break;
635
636 case LF_EDARG: // text immediately following '%{arg}'
637 if (*str == '[') {
638 cformat = LF_STEXPR;
639 var = str + 1; // store expr in variable name
640 break;
641 }
642 else if (isalnum((unsigned char)*str)) { // variable name
643 cformat = LF_VAR;
644 var = str;
645 break;
646 }
647 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
648 goto fail;
649
650 case LF_STEXPR: // text immediately following '%['
651 if (*str == ']') { // end of arg
652 cformat = LF_EDEXPR;
653 var_len = str - var;
654 *str = 0; // needed for parsing the expression
655 }
656 break;
657
658 case LF_VAR: // text part of a variable name
659 var_len = str - var;
660 if (!isalnum((unsigned char)*str))
661 cformat = LF_INIT; // not variable name anymore
662 break;
663
664 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
665 cformat = LF_INIT;
666 }
667
668 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
669 switch (*str) {
670 case '%': cformat = LF_STARTVAR; break;
671 case ' ': cformat = LF_SEPARATOR; break;
672 case 0 : cformat = LF_END; break;
673 default : cformat = LF_TEXT; break;
674 }
675 }
676
677 if (cformat != pformat || pformat == LF_SEPARATOR) {
678 switch (pformat) {
679 case LF_VAR:
680 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
681 goto fail;
682 break;
683 case LF_STEXPR:
684 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err))
685 goto fail;
686 break;
687 case LF_TEXT:
688 case LF_SEPARATOR:
689 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
690 goto fail;
691 break;
692 }
693 sp = str; /* new start of text at every state switch and at every separator */
694 }
695 }
696
697 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
698 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
699 goto fail;
700 }
701 free(backfmt);
702
703 return 1;
704 fail:
705 free(backfmt);
706 return 0;
707 }
708
709 /*
710 * Parse the first range of indexes from a string made of a list of comma seperated
711 * ranges of indexes. Note that an index may be considered as a particular range
712 * with a high limit to the low limit.
713 */
get_logsrv_smp_range(unsigned int * low,unsigned int * high,char ** arg,char ** err)714 int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
715 {
716 char *end, *p;
717
718 *low = *high = 0;
719
720 p = *arg;
721 end = strchr(p, ',');
722 if (!end)
723 end = p + strlen(p);
724
725 *high = *low = read_uint((const char **)&p, end);
726 if (!*low || (p != end && *p != '-'))
727 goto err;
728
729 if (p == end)
730 goto done;
731
732 p++;
733 *high = read_uint((const char **)&p, end);
734 if (!*high || *high <= *low || p != end)
735 goto err;
736
737 done:
738 if (*end == ',')
739 end++;
740 *arg = end;
741 return 1;
742
743 err:
744 memprintf(err, "wrong sample range '%s'", *arg);
745 return 0;
746 }
747
748 /*
749 * Returns 1 if the range defined by <low> and <high> overlaps
750 * one of them in <rgs> array of ranges with <sz> the size of this
751 * array, 0 if not.
752 */
smp_log_ranges_overlap(struct smp_log_range * rgs,size_t sz,unsigned int low,unsigned int high,char ** err)753 int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
754 unsigned int low, unsigned int high, char **err)
755 {
756 size_t i;
757
758 for (i = 0; i < sz; i++) {
759 if ((low >= rgs[i].low && low <= rgs[i].high) ||
760 (high >= rgs[i].low && high <= rgs[i].high)) {
761 memprintf(err, "ranges are overlapping");
762 return 1;
763 }
764 }
765
766 return 0;
767 }
768
smp_log_range_cmp(const void * a,const void * b)769 int smp_log_range_cmp(const void *a, const void *b)
770 {
771 const struct smp_log_range *rg_a = a;
772 const struct smp_log_range *rg_b = b;
773
774 if (rg_a->high < rg_b->low)
775 return -1;
776 else if (rg_a->low > rg_b->high)
777 return 1;
778
779 return 0;
780 }
781
782 /*
783 * Parse "log" keyword and update <logsrvs> list accordingly.
784 *
785 * When <do_del> is set, it means the "no log" line was parsed, so all log
786 * servers in <logsrvs> are released.
787 *
788 * Otherwise, we try to parse the "log" line. First of all, when the list is not
789 * the global one, we look for the parameter "global". If we find it,
790 * global.logsrvs is copied. Else we parse each arguments.
791 *
792 * The function returns 1 in success case, otherwise, it returns 0 and err is
793 * filled.
794 */
parse_logsrv(char ** args,struct list * logsrvs,int do_del,char ** err)795 int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
796 {
797 struct sockaddr_storage *sk;
798 struct logsrv *logsrv = NULL;
799 int port1, port2;
800 int cur_arg;
801
802 /*
803 * "no log": delete previous herited or defined syslog
804 * servers.
805 */
806 if (do_del) {
807 struct logsrv *back;
808
809 if (*(args[1]) != 0) {
810 memprintf(err, "'no log' does not expect arguments");
811 goto error;
812 }
813
814 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
815 LIST_DEL(&logsrv->list);
816 free(logsrv);
817 }
818 return 1;
819 }
820
821 /*
822 * "log global": copy global.logrsvs linked list to the end of logsrvs
823 * list. But first, we check (logsrvs != global.logsrvs).
824 */
825 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
826 if (logsrvs == &global.logsrvs) {
827 memprintf(err, "'global' is not supported for a global syslog server");
828 goto error;
829 }
830 list_for_each_entry(logsrv, &global.logsrvs, list) {
831 struct logsrv *node;
832
833 list_for_each_entry(node, logsrvs, list) {
834 if (node->ref == logsrv)
835 goto skip_logsrv;
836 }
837
838 node = malloc(sizeof(*node));
839 memcpy(node, logsrv, sizeof(struct logsrv));
840 node->ref = logsrv;
841 LIST_INIT(&node->list);
842 LIST_ADDQ(logsrvs, &node->list);
843
844 skip_logsrv:
845 continue;
846 }
847 return 1;
848 }
849
850 /*
851 * "log <address> ...: parse a syslog server line
852 */
853 if (*(args[1]) == 0 || *(args[2]) == 0) {
854 memprintf(err, "expects <address> and <facility> %s as arguments",
855 ((logsrvs == &global.logsrvs) ? "" : "or global"));
856 goto error;
857 }
858
859 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
860 if (strcmp(args[1], "stdout") == 0)
861 args[1] = "fd@1";
862 else if (strcmp(args[1], "stderr") == 0)
863 args[1] = "fd@2";
864
865 logsrv = calloc(1, sizeof(*logsrv));
866 if (!logsrv) {
867 memprintf(err, "out of memory");
868 goto error;
869 }
870
871 /* skip address for now, it will be parsed at the end */
872 cur_arg = 2;
873
874 /* just after the address, a length may be specified */
875 logsrv->maxlen = MAX_SYSLOG_LEN;
876 if (strcmp(args[cur_arg], "len") == 0) {
877 int len = atoi(args[cur_arg+1]);
878 if (len < 80 || len > 65535) {
879 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
880 args[cur_arg+1]);
881 goto error;
882 }
883 logsrv->maxlen = len;
884 cur_arg += 2;
885 }
886 if (logsrv->maxlen > global.max_syslog_len)
887 global.max_syslog_len = logsrv->maxlen;
888
889 /* after the length, a format may be specified */
890 if (strcmp(args[cur_arg], "format") == 0) {
891 logsrv->format = get_log_format(args[cur_arg+1]);
892 if (logsrv->format < 0) {
893 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
894 goto error;
895 }
896 cur_arg += 2;
897 }
898
899 if (strcmp(args[cur_arg], "sample") == 0) {
900 unsigned low, high;
901 char *p, *beg, *end, *smp_sz_str;
902 struct smp_log_range *smp_rgs = NULL;
903 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
904
905 p = args[cur_arg+1];
906 smp_sz_str = strchr(p, ':');
907 if (!smp_sz_str) {
908 memprintf(err, "Missing sample size");
909 goto error;
910 }
911
912 *smp_sz_str++ = '\0';
913
914 end = p + strlen(p);
915
916 while (p != end) {
917 if (!get_logsrv_smp_range(&low, &high, &p, err))
918 goto error;
919
920 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
921 goto error;
922
923 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
924 if (!smp_rgs) {
925 memprintf(err, "out of memory error");
926 goto error;
927 }
928
929 smp_rgs[smp_rgs_sz].low = low;
930 smp_rgs[smp_rgs_sz].high = high;
931 smp_rgs[smp_rgs_sz].sz = high - low + 1;
932 smp_rgs[smp_rgs_sz].curr_idx = 0;
933 if (smp_rgs[smp_rgs_sz].high > smp_sz)
934 smp_sz = smp_rgs[smp_rgs_sz].high;
935 smp_rgs_sz++;
936 }
937
938 if (smp_rgs == NULL) {
939 memprintf(err, "no sampling ranges given");
940 goto error;
941 }
942
943 beg = smp_sz_str;
944 end = beg + strlen(beg);
945 new_smp_sz = read_uint((const char **)&beg, end);
946 if (!new_smp_sz || beg != end) {
947 memprintf(err, "wrong sample size '%s' for sample range '%s'",
948 smp_sz_str, args[cur_arg+1]);
949 goto error;
950 }
951
952 if (new_smp_sz < smp_sz) {
953 memprintf(err, "sample size %zu should be greater or equal to "
954 "%zu the maximum of the high ranges limits",
955 new_smp_sz, smp_sz);
956 goto error;
957 }
958 smp_sz = new_smp_sz;
959
960 /* Let's order <smp_rgs> array. */
961 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
962
963 logsrv->lb.smp_rgs = smp_rgs;
964 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
965 logsrv->lb.smp_sz = smp_sz;
966
967 cur_arg += 2;
968 }
969 HA_SPIN_INIT(&logsrv->lock);
970 /* parse the facility */
971 logsrv->facility = get_log_facility(args[cur_arg]);
972 if (logsrv->facility < 0) {
973 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
974 goto error;
975 }
976 cur_arg++;
977
978 /* parse the max syslog level (default: debug) */
979 logsrv->level = 7;
980 if (*(args[cur_arg])) {
981 logsrv->level = get_log_level(args[cur_arg]);
982 if (logsrv->level < 0) {
983 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
984 goto error;
985 }
986 cur_arg++;
987 }
988
989 /* parse the limit syslog level (default: emerg) */
990 logsrv->minlvl = 0;
991 if (*(args[cur_arg])) {
992 logsrv->minlvl = get_log_level(args[cur_arg]);
993 if (logsrv->minlvl < 0) {
994 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
995 goto error;
996 }
997 cur_arg++;
998 }
999
1000 /* Too many args */
1001 if (*(args[cur_arg])) {
1002 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
1003 goto error;
1004 }
1005
1006 /* now, back to the address */
1007 logsrv->type = LOG_TARGET_DGRAM;
1008 if (strncmp(args[1], "ring@", 5) == 0) {
1009 struct sink *sink = sink_find(args[1] + 5);
1010
1011 if (!sink || sink->type != SINK_TYPE_BUFFER) {
1012 memprintf(err, "cannot find ring buffer '%s'", args[1] + 5);
1013 goto error;
1014 }
1015
1016 logsrv->addr.ss_family = AF_UNSPEC;
1017 logsrv->type = LOG_TARGET_BUFFER;
1018 logsrv->ring = sink->ctx.ring;
1019 goto done;
1020 }
1021
1022 if (strncmp(args[1], "fd@", 3) == 0)
1023 logsrv->type = LOG_TARGET_FD;
1024
1025 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1026 if (!sk)
1027 goto error;
1028 logsrv->addr = *sk;
1029
1030 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1031 if (port1 != port2) {
1032 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1033 goto error;
1034 }
1035 logsrv->addr = *sk;
1036 if (!port1)
1037 set_host_port(&logsrv->addr, SYSLOG_PORT);
1038 }
1039 done:
1040 LIST_ADDQ(logsrvs, &logsrv->list);
1041 return 1;
1042
1043 error:
1044 free(logsrv);
1045 return 0;
1046 }
1047
1048
1049 /* Generic function to display messages prefixed by a label */
print_message(const char * label,const char * fmt,va_list argp)1050 static void print_message(const char *label, const char *fmt, va_list argp)
1051 {
1052 struct tm tm;
1053 char *head, *msg;
1054
1055 head = msg = NULL;
1056
1057 get_localtime(date.tv_sec, &tm);
1058 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1059 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1060 memvprintf(&msg, fmt, argp);
1061
1062 if (global.mode & MODE_STARTING) {
1063 if (unlikely(!startup_logs))
1064 startup_logs = ring_new(STARTUP_LOG_SIZE);
1065
1066 if (likely(startup_logs)) {
1067 struct ist m[2];
1068
1069 m[0] = ist(head);
1070 m[1] = ist(msg);
1071 /* trim the trailing '\n' */
1072 if (m[1].len > 0 && m[1].ptr[m[1].len - 1] == '\n')
1073 m[1].len--;
1074 ring_write(startup_logs, ~0, 0, 0, m, 2);
1075 }
1076 }
1077
1078 fprintf(stderr, "%s%s", head, msg);
1079 fflush(stderr);
1080
1081 free(head);
1082 free(msg);
1083 }
1084
1085 /*
1086 * Displays the message on stderr with the date and pid. Overrides the quiet
1087 * mode during startup.
1088 */
ha_alert(const char * fmt,...)1089 void ha_alert(const char *fmt, ...)
1090 {
1091 va_list argp;
1092
1093 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1094 va_start(argp, fmt);
1095 print_message("ALERT", fmt, argp);
1096 va_end(argp);
1097 }
1098 }
1099
1100
1101 /*
1102 * Displays the message on stderr with the date and pid.
1103 */
ha_warning(const char * fmt,...)1104 void ha_warning(const char *fmt, ...)
1105 {
1106 va_list argp;
1107
1108 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1109 va_start(argp, fmt);
1110 print_message("WARNING", fmt, argp);
1111 va_end(argp);
1112 }
1113 }
1114
1115 /*
1116 * Displays the message on stderr with the date and pid.
1117 */
ha_notice(const char * fmt,...)1118 void ha_notice(const char *fmt, ...)
1119 {
1120 va_list argp;
1121
1122 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1123 va_start(argp, fmt);
1124 print_message("NOTICE", fmt, argp);
1125 va_end(argp);
1126 }
1127 }
1128
1129 /*
1130 * Displays the message on <out> only if quiet mode is not set.
1131 */
qfprintf(FILE * out,const char * fmt,...)1132 void qfprintf(FILE *out, const char *fmt, ...)
1133 {
1134 va_list argp;
1135
1136 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1137 va_start(argp, fmt);
1138 vfprintf(out, fmt, argp);
1139 fflush(out);
1140 va_end(argp);
1141 }
1142 }
1143
1144 /*
1145 * returns log format for <fmt> or -1 if not found.
1146 */
get_log_format(const char * fmt)1147 int get_log_format(const char *fmt)
1148 {
1149 int format;
1150
1151 format = LOG_FORMATS - 1;
1152 while (format >= 0 && strcmp(log_formats[format].name, fmt))
1153 format--;
1154
1155 return format;
1156 }
1157
1158 /*
1159 * returns log level for <lev> or -1 if not found.
1160 */
get_log_level(const char * lev)1161 int get_log_level(const char *lev)
1162 {
1163 int level;
1164
1165 level = NB_LOG_LEVELS - 1;
1166 while (level >= 0 && strcmp(log_levels[level], lev))
1167 level--;
1168
1169 return level;
1170 }
1171
1172 /*
1173 * returns log facility for <fac> or -1 if not found.
1174 */
get_log_facility(const char * fac)1175 int get_log_facility(const char *fac)
1176 {
1177 int facility;
1178
1179 facility = NB_LOG_FACILITIES - 1;
1180 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1181 facility--;
1182
1183 return facility;
1184 }
1185
1186 /*
1187 * Encode the string.
1188 *
1189 * When using the +E log format option, it will try to escape '"\]'
1190 * characters with '\' as prefix. The same prefix should not be used as
1191 * <escape>.
1192 */
lf_encode_string(char * start,char * stop,const char escape,const long * map,const char * string,struct logformat_node * node)1193 static char *lf_encode_string(char *start, char *stop,
1194 const char escape, const long *map,
1195 const char *string,
1196 struct logformat_node *node)
1197 {
1198 if (node->options & LOG_OPT_ESC) {
1199 if (start < stop) {
1200 stop--; /* reserve one byte for the final '\0' */
1201 while (start < stop && *string != '\0') {
1202 if (!ha_bit_test((unsigned char)(*string), map)) {
1203 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
1204 *start++ = *string;
1205 else {
1206 if (start + 2 >= stop)
1207 break;
1208 *start++ = '\\';
1209 *start++ = *string;
1210 }
1211 }
1212 else {
1213 if (start + 3 >= stop)
1214 break;
1215 *start++ = escape;
1216 *start++ = hextab[(*string >> 4) & 15];
1217 *start++ = hextab[*string & 15];
1218 }
1219 string++;
1220 }
1221 *start = '\0';
1222 }
1223 }
1224 else {
1225 return encode_string(start, stop, escape, map, string);
1226 }
1227
1228 return start;
1229 }
1230
1231 /*
1232 * Encode the chunk.
1233 *
1234 * When using the +E log format option, it will try to escape '"\]'
1235 * characters with '\' as prefix. The same prefix should not be used as
1236 * <escape>.
1237 */
lf_encode_chunk(char * start,char * stop,const char escape,const long * map,const struct buffer * chunk,struct logformat_node * node)1238 static char *lf_encode_chunk(char *start, char *stop,
1239 const char escape, const long *map,
1240 const struct buffer *chunk,
1241 struct logformat_node *node)
1242 {
1243 char *str, *end;
1244
1245 if (node->options & LOG_OPT_ESC) {
1246 if (start < stop) {
1247 str = chunk->area;
1248 end = chunk->area + chunk->data;
1249
1250 stop--; /* reserve one byte for the final '\0' */
1251 while (start < stop && str < end) {
1252 if (!ha_bit_test((unsigned char)(*str), map)) {
1253 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
1254 *start++ = *str;
1255 else {
1256 if (start + 2 >= stop)
1257 break;
1258 *start++ = '\\';
1259 *start++ = *str;
1260 }
1261 }
1262 else {
1263 if (start + 3 >= stop)
1264 break;
1265 *start++ = escape;
1266 *start++ = hextab[(*str >> 4) & 15];
1267 *start++ = hextab[*str & 15];
1268 }
1269 str++;
1270 }
1271 *start = '\0';
1272 }
1273 }
1274 else {
1275 return encode_chunk(start, stop, escape, map, chunk);
1276 }
1277
1278 return start;
1279 }
1280
1281 /*
1282 * Write a string in the log string
1283 * Take cares of quote and escape options
1284 *
1285 * Return the address of the \0 character, or NULL on error
1286 */
lf_text_len(char * dst,const char * src,size_t len,size_t size,const struct logformat_node * node)1287 char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const struct logformat_node *node)
1288 {
1289 if (size < 2)
1290 return NULL;
1291
1292 if (node->options & LOG_OPT_QUOTE) {
1293 *(dst++) = '"';
1294 size--;
1295 }
1296
1297 if (src && len) {
1298 if (++len > size)
1299 len = size;
1300 if (node->options & LOG_OPT_ESC) {
1301 char *ret;
1302
1303 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
1304 if (ret == NULL || *ret != '\0')
1305 return NULL;
1306 len = ret - dst;
1307 }
1308 else {
1309 len = strlcpy2(dst, src, len);
1310 }
1311
1312 size -= len;
1313 dst += len;
1314 }
1315 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1316 if (size < 2)
1317 return NULL;
1318 *(dst++) = '-';
1319 }
1320
1321 if (node->options & LOG_OPT_QUOTE) {
1322 if (size < 2)
1323 return NULL;
1324 *(dst++) = '"';
1325 }
1326
1327 *dst = '\0';
1328 return dst;
1329 }
1330
lf_text(char * dst,const char * src,size_t size,const struct logformat_node * node)1331 static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
1332 {
1333 return lf_text_len(dst, src, size, size, node);
1334 }
1335
1336 /*
1337 * Write a IP address to the log string
1338 * +X option write in hexadecimal notation, most signifant byte on the left
1339 */
lf_ip(char * dst,const struct sockaddr * sockaddr,size_t size,const struct logformat_node * node)1340 char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
1341 {
1342 char *ret = dst;
1343 int iret;
1344 char pn[INET6_ADDRSTRLEN];
1345
1346 if (node->options & LOG_OPT_HEXA) {
1347 unsigned char *addr = NULL;
1348 switch (sockaddr->sa_family) {
1349 case AF_INET:
1350 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1351 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1352 break;
1353 case AF_INET6:
1354 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1355 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1356 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1357 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1358 break;
1359 default:
1360 return NULL;
1361 }
1362 if (iret < 0 || iret > size)
1363 return NULL;
1364 ret += iret;
1365 } else {
1366 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1367 ret = lf_text(dst, pn, size, node);
1368 if (ret == NULL)
1369 return NULL;
1370 }
1371 return ret;
1372 }
1373
1374 /*
1375 * Write a port to the log
1376 * +X option write in hexadecimal notation, most signifant byte on the left
1377 */
lf_port(char * dst,const struct sockaddr * sockaddr,size_t size,const struct logformat_node * node)1378 char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
1379 {
1380 char *ret = dst;
1381 int iret;
1382
1383 if (node->options & LOG_OPT_HEXA) {
1384 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1385 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1386 if (iret < 0 || iret > size)
1387 return NULL;
1388 ret += iret;
1389 } else {
1390 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1391 if (ret == NULL)
1392 return NULL;
1393 }
1394 return ret;
1395 }
1396
1397 /* Re-generate time-based part of the syslog header in RFC3164 format at
1398 * the beginning of logheader once a second and return the pointer to the
1399 * first character after it.
1400 */
update_log_hdr(const time_t time)1401 static char *update_log_hdr(const time_t time)
1402 {
1403 static THREAD_LOCAL long tvsec;
1404 static THREAD_LOCAL struct buffer host = { };
1405 static THREAD_LOCAL int sep = 0;
1406
1407 if (unlikely(time != tvsec || logheader_end == NULL)) {
1408 /* this string is rebuild only once a second */
1409 struct tm tm;
1410 int hdr_len;
1411
1412 tvsec = time;
1413 get_localtime(tvsec, &tm);
1414
1415 if (unlikely(global.log_send_hostname != host.area)) {
1416 host.area = global.log_send_hostname;
1417 host.data = host.area ? strlen(host.area) : 0;
1418 sep = host.data ? 1 : 0;
1419 }
1420
1421 hdr_len = snprintf(logheader, global.max_syslog_len,
1422 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
1423 monthname[tm.tm_mon],
1424 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
1425 (int)host.data, host.area, sep, "");
1426 /* WARNING: depending upon implementations, snprintf may return
1427 * either -1 or the number of bytes that would be needed to store
1428 * the total message. In both cases, we must adjust it.
1429 */
1430 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1431 hdr_len = global.max_syslog_len;
1432
1433 logheader_end = logheader + hdr_len;
1434 }
1435
1436 logheader_end[0] = 0; // ensure we get rid of any previous attempt
1437
1438 return logheader_end;
1439 }
1440
1441 /* Re-generate time-based part of the syslog header in RFC5424 format at
1442 * the beginning of logheader_rfc5424 once a second and return the pointer
1443 * to the first character after it.
1444 */
update_log_hdr_rfc5424(const time_t time)1445 static char *update_log_hdr_rfc5424(const time_t time)
1446 {
1447 static THREAD_LOCAL long tvsec;
1448 const char *gmt_offset;
1449
1450 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
1451 /* this string is rebuild only once a second */
1452 struct tm tm;
1453 int hdr_len;
1454
1455 tvsec = time;
1456 get_localtime(tvsec, &tm);
1457 gmt_offset = get_gmt_offset(time, &tm);
1458
1459 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
1460 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
1461 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
1462 tm.tm_hour, tm.tm_min, tm.tm_sec,
1463 gmt_offset, gmt_offset+3,
1464 global.log_send_hostname ? global.log_send_hostname : hostname);
1465 /* WARNING: depending upon implementations, snprintf may return
1466 * either -1 or the number of bytes that would be needed to store
1467 * the total message. In both cases, we must adjust it.
1468 */
1469 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1470 hdr_len = global.max_syslog_len;
1471
1472 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
1473 }
1474
1475 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
1476
1477 return logheader_rfc5424_end;
1478 }
1479
1480 /*
1481 * This function sends the syslog message using a printf format string. It
1482 * expects an LF-terminated message.
1483 */
send_log(struct proxy * p,int level,const char * format,...)1484 void send_log(struct proxy *p, int level, const char *format, ...)
1485 {
1486 va_list argp;
1487 int data_len;
1488
1489 if (level < 0 || format == NULL || logline == NULL)
1490 return;
1491
1492 va_start(argp, format);
1493 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
1494 if (data_len < 0 || data_len > global.max_syslog_len)
1495 data_len = global.max_syslog_len;
1496 va_end(argp);
1497
1498 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1499 logline, data_len, default_rfc5424_sd_log_format, 2);
1500 }
1501
1502 /*
1503 * This function sends a syslog message to <logsrv>.
1504 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1505 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1506 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
1507 * It overrides the last byte of the message vector with an LF character.
1508 * Does not return any error,
1509 */
__do_send_log(struct logsrv * logsrv,int nblogger,char * pid_str,size_t pid_size,int level,char * message,size_t size,char * sd,size_t sd_size,char * tag_str,size_t tag_size)1510 static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1511 int level, char *message, size_t size, char *sd, size_t sd_size,
1512 char *tag_str, size_t tag_size)
1513 {
1514 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1515 static THREAD_LOCAL struct msghdr msghdr = {
1516 //.msg_iov = iovec,
1517 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1518 };
1519 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1520 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1521 static THREAD_LOCAL char *dataptr = NULL;
1522 time_t time = date.tv_sec;
1523 char *hdr, *hdr_ptr;
1524 size_t hdr_size;
1525 int fac_level;
1526 int *plogfd;
1527 char *pid_sep1 = "", *pid_sep2 = "";
1528 char logheader_short[3];
1529 int sent;
1530 int maxlen;
1531 int hdr_max = 0;
1532 int tag_max = 0;
1533 int pid_sep1_max = 0;
1534 int pid_max = 0;
1535 int pid_sep2_max = 0;
1536 int sd_max = 0;
1537 int max = 0;
1538
1539 msghdr.msg_iov = iovec;
1540
1541 dataptr = message;
1542
1543 if (logsrv->type == LOG_TARGET_FD) {
1544 /* the socket's address is a file descriptor */
1545 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1546 }
1547 else if (logsrv->type == LOG_TARGET_BUFFER) {
1548 plogfd = NULL;
1549 }
1550 else if (logsrv->addr.ss_family == AF_UNIX)
1551 plogfd = &logfdunix;
1552 else
1553 plogfd = &logfdinet;
1554
1555 if (plogfd && unlikely(*plogfd < 0)) {
1556 /* socket not successfully initialized yet */
1557 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1558 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1559 static char once;
1560
1561 if (!once) {
1562 once = 1; /* note: no need for atomic ops here */
1563 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1564 nblogger, strerror(errno), errno);
1565 }
1566 return;
1567 } else {
1568 /* we don't want to receive anything on this socket */
1569 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1570 /* does nothing under Linux, maybe needed for others */
1571 shutdown(*plogfd, SHUT_RD);
1572 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1573 }
1574 }
1575
1576 switch (logsrv->format) {
1577 case LOG_FORMAT_RFC3164:
1578 hdr = logheader;
1579 hdr_ptr = update_log_hdr(time);
1580 break;
1581
1582 case LOG_FORMAT_RFC5424:
1583 hdr = logheader_rfc5424;
1584 hdr_ptr = update_log_hdr_rfc5424(time);
1585 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1586 break;
1587
1588 case LOG_FORMAT_SHORT:
1589 /* all fields are known, skip the header generation */
1590 hdr = logheader_short;
1591 hdr[0] = '<';
1592 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1593 hdr[2] = '>';
1594 hdr_ptr = hdr;
1595 hdr_max = 3;
1596 maxlen = logsrv->maxlen - hdr_max;
1597 max = MIN(size, maxlen) - 1;
1598 goto send;
1599
1600 case LOG_FORMAT_RAW:
1601 /* all fields are known, skip the header generation */
1602 hdr_ptr = hdr = "";
1603 hdr_max = 0;
1604 maxlen = logsrv->maxlen;
1605 max = MIN(size, maxlen) - 1;
1606 goto send;
1607
1608 default:
1609 return; /* must never happen */
1610 }
1611
1612 hdr_size = hdr_ptr - hdr;
1613
1614 /* For each target, we may have a different facility.
1615 * We can also have a different log level for each message.
1616 * This induces variations in the message header length.
1617 * Since we don't want to recompute it each time, nor copy it every
1618 * time, we only change the facility in the pre-computed header,
1619 * and we change the pointer to the header accordingly.
1620 */
1621 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1622 hdr_ptr = hdr + 3; /* last digit of the log level */
1623 do {
1624 *hdr_ptr = '0' + fac_level % 10;
1625 fac_level /= 10;
1626 hdr_ptr--;
1627 } while (fac_level && hdr_ptr > hdr);
1628 *hdr_ptr = '<';
1629
1630 hdr_max = hdr_size - (hdr_ptr - hdr);
1631
1632 /* time-based header */
1633 if (unlikely(hdr_size >= logsrv->maxlen)) {
1634 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1635 sd_max = 0;
1636 goto send;
1637 }
1638
1639 maxlen = logsrv->maxlen - hdr_max;
1640
1641 /* tag */
1642 tag_max = tag_size;
1643 if (unlikely(tag_max >= maxlen)) {
1644 tag_max = maxlen - 1;
1645 sd_max = 0;
1646 goto send;
1647 }
1648
1649 maxlen -= tag_max;
1650
1651 /* first pid separator */
1652 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1653 if (unlikely(pid_sep1_max >= maxlen)) {
1654 pid_sep1_max = maxlen - 1;
1655 sd_max = 0;
1656 goto send;
1657 }
1658
1659 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1660 maxlen -= pid_sep1_max;
1661
1662 /* pid */
1663 pid_max = pid_size;
1664 if (unlikely(pid_size >= maxlen)) {
1665 pid_size = maxlen - 1;
1666 sd_max = 0;
1667 goto send;
1668 }
1669
1670 maxlen -= pid_size;
1671
1672 /* second pid separator */
1673 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1674 if (unlikely(pid_sep2_max >= maxlen)) {
1675 pid_sep2_max = maxlen - 1;
1676 sd_max = 0;
1677 goto send;
1678 }
1679
1680 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1681 maxlen -= pid_sep2_max;
1682
1683 /* structured-data */
1684 if (sd_max >= maxlen) {
1685 sd_max = maxlen - 1;
1686 goto send;
1687 }
1688
1689 max = MIN(size, maxlen - sd_max) - 1;
1690 send:
1691 if (logsrv->addr.ss_family == AF_UNSPEC) {
1692 /* the target is a file descriptor or a ring buffer */
1693 struct ist msg[7];
1694
1695 msg[0].ptr = hdr_ptr; msg[0].len = hdr_max;
1696 msg[1].ptr = tag_str; msg[1].len = tag_max;
1697 msg[2].ptr = pid_sep1; msg[2].len = pid_sep1_max;
1698 msg[3].ptr = pid_str; msg[3].len = pid_max;
1699 msg[4].ptr = pid_sep2; msg[4].len = pid_sep2_max;
1700 msg[5].ptr = sd; msg[5].len = sd_max;
1701 msg[6].ptr = dataptr; msg[6].len = max;
1702
1703 if (logsrv->type == LOG_TARGET_BUFFER)
1704 sent = ring_write(logsrv->ring, ~0, NULL, 0, msg, 7);
1705 else /* LOG_TARGET_FD */
1706 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
1707 }
1708 else {
1709 iovec[0].iov_base = hdr_ptr;
1710 iovec[0].iov_len = hdr_max;
1711 iovec[1].iov_base = tag_str;
1712 iovec[1].iov_len = tag_max;
1713 iovec[2].iov_base = pid_sep1;
1714 iovec[2].iov_len = pid_sep1_max;
1715 iovec[3].iov_base = pid_str;
1716 iovec[3].iov_len = pid_max;
1717 iovec[4].iov_base = pid_sep2;
1718 iovec[4].iov_len = pid_sep2_max;
1719 iovec[5].iov_base = sd;
1720 iovec[5].iov_len = sd_max;
1721 iovec[6].iov_base = dataptr;
1722 iovec[6].iov_len = max;
1723 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1724 iovec[7].iov_len = 1;
1725
1726 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1727 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
1728
1729 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1730 }
1731
1732 if (sent < 0) {
1733 static char once;
1734
1735 if (errno == EAGAIN)
1736 _HA_ATOMIC_ADD(&dropped_logs, 1);
1737 else if (!once) {
1738 once = 1; /* note: no need for atomic ops here */
1739 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1740 nblogger, strerror(errno), errno);
1741 }
1742 }
1743 }
1744
1745 /*
1746 * This function sends a syslog message.
1747 * It doesn't care about errors nor does it report them.
1748 * The arguments <sd> and <sd_size> are used for the structured-data part
1749 * in RFC5424 formatted syslog messages.
1750 */
__send_log(struct list * logsrvs,struct buffer * tag,int level,char * message,size_t size,char * sd,size_t sd_size)1751 void __send_log(struct list *logsrvs, struct buffer *tag, int level,
1752 char *message, size_t size, char *sd, size_t sd_size)
1753 {
1754 struct logsrv *logsrv;
1755 int nblogger;
1756 static THREAD_LOCAL int curr_pid;
1757 static THREAD_LOCAL char pidstr[100];
1758 static THREAD_LOCAL struct buffer pid;
1759
1760 if (logsrvs == NULL) {
1761 if (!LIST_ISEMPTY(&global.logsrvs)) {
1762 logsrvs = &global.logsrvs;
1763 }
1764 }
1765 if (!tag || !tag->area)
1766 tag = &global.log_tag;
1767
1768 if (!logsrvs || LIST_ISEMPTY(logsrvs))
1769 return;
1770
1771 if (unlikely(curr_pid != getpid())) {
1772 curr_pid = getpid();
1773 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1774 chunk_initstr(&pid, pidstr);
1775 }
1776
1777 /* Send log messages to syslog server. */
1778 nblogger = 0;
1779 list_for_each_entry(logsrv, logsrvs, list) {
1780 static THREAD_LOCAL int in_range = 1;
1781
1782 /* we can filter the level of the messages that are sent to each logger */
1783 if (level > logsrv->level)
1784 continue;
1785
1786 if (logsrv->lb.smp_rgs) {
1787 struct smp_log_range *curr_rg;
1788
1789 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1790 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1791 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1792 if (in_range) {
1793 /* Let's consume this range. */
1794 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1795 if (!curr_rg->curr_idx) {
1796 /* If consumed, let's select the next range. */
1797 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1798 }
1799 }
1800 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1801 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1802 }
1803 if (in_range)
1804 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1805 message, size, sd, sd_size, tag->area, tag->data);
1806 }
1807 }
1808
1809
1810 const char sess_cookie[8] = "NIDVEOU7"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */
1811 const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1812 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1813 Set-cookie Updated, unknown, unknown */
1814
1815 /*
1816 * try to write a character if there is enough space, or goto out
1817 */
1818 #define LOGCHAR(x) do { \
1819 if (tmplog < dst + maxsize - 1) { \
1820 *(tmplog++) = (x); \
1821 } else { \
1822 goto out; \
1823 } \
1824 } while(0)
1825
1826
1827 /* Initializes some log data at boot */
init_log()1828 static void init_log()
1829 {
1830 char *tmp;
1831 int i;
1832
1833 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1834 * inside PARAM-VALUE should be escaped with '\' as prefix.
1835 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1836 * details.
1837 */
1838 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1839
1840 tmp = "\"\\]";
1841 while (*tmp) {
1842 ha_bit_set(*tmp, rfc5424_escape_map);
1843 tmp++;
1844 }
1845
1846 /* initialize the log header encoding map : '{|}"#' should be encoded with
1847 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1848 * URL encoding only requires '"', '#' to be encoded as well as non-
1849 * printable characters above.
1850 */
1851 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1852 memset(url_encode_map, 0, sizeof(url_encode_map));
1853 for (i = 0; i < 32; i++) {
1854 ha_bit_set(i, hdr_encode_map);
1855 ha_bit_set(i, url_encode_map);
1856 }
1857 for (i = 127; i < 256; i++) {
1858 ha_bit_set(i, hdr_encode_map);
1859 ha_bit_set(i, url_encode_map);
1860 }
1861
1862 tmp = "\"#{|}";
1863 while (*tmp) {
1864 ha_bit_set(*tmp, hdr_encode_map);
1865 tmp++;
1866 }
1867
1868 tmp = "\"#";
1869 while (*tmp) {
1870 ha_bit_set(*tmp, url_encode_map);
1871 tmp++;
1872 }
1873
1874 /* initialize the http header encoding map. The draft httpbis define the
1875 * header content as:
1876 *
1877 * HTTP-message = start-line
1878 * *( header-field CRLF )
1879 * CRLF
1880 * [ message-body ]
1881 * header-field = field-name ":" OWS field-value OWS
1882 * field-value = *( field-content / obs-fold )
1883 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1884 * obs-fold = CRLF 1*( SP / HTAB )
1885 * field-vchar = VCHAR / obs-text
1886 * VCHAR = %x21-7E
1887 * obs-text = %x80-FF
1888 *
1889 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1890 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
1891 * "obs-fold" is voluntarily forgotten because haproxy remove this.
1892 */
1893 memset(http_encode_map, 0, sizeof(http_encode_map));
1894 for (i = 0x00; i <= 0x08; i++)
1895 ha_bit_set(i, http_encode_map);
1896 for (i = 0x0a; i <= 0x1f; i++)
1897 ha_bit_set(i, http_encode_map);
1898 ha_bit_set(0x7f, http_encode_map);
1899 }
1900
1901 INITCALL0(STG_PREPARE, init_log);
1902
1903 /* Initialize log buffers used for syslog messages */
init_log_buffers()1904 int init_log_buffers()
1905 {
1906 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
1907 logheader_end = NULL;
1908 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
1909 logheader_rfc5424_end = NULL;
1910 logline = my_realloc2(logline, global.max_syslog_len + 1);
1911 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1912 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1913 return 0;
1914 return 1;
1915 }
1916
1917 /* Deinitialize log buffers used for syslog messages */
deinit_log_buffers()1918 void deinit_log_buffers()
1919 {
1920 free(logheader);
1921 free(logheader_rfc5424);
1922 free(logline);
1923 free(logline_rfc5424);
1924 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
1925 logheader = NULL;
1926 logheader_rfc5424 = NULL;
1927 logline = NULL;
1928 logline_rfc5424 = NULL;
1929 }
1930
1931 /* Builds a log line in <dst> based on <list_format>, and stops before reaching
1932 * <maxsize> characters. Returns the size of the output string in characters,
1933 * not counting the trailing zero which is always added if the resulting size
1934 * is not zero. It requires a valid session and optionally a stream. If the
1935 * stream is NULL, default values will be assumed for the stream part.
1936 */
sess_build_logline(struct session * sess,struct stream * s,char * dst,size_t maxsize,struct list * list_format)1937 int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct list *list_format)
1938 {
1939 struct proxy *fe = sess->fe;
1940 struct proxy *be;
1941 struct http_txn *txn;
1942 const struct strm_logs *logs;
1943 struct connection *be_conn;
1944 unsigned int s_flags;
1945 unsigned int uniq_id;
1946 struct buffer chunk;
1947 char *uri;
1948 char *spc;
1949 char *qmark;
1950 char *end;
1951 struct tm tm;
1952 int t_request;
1953 int hdr;
1954 int last_isspace = 1;
1955 int nspaces = 0;
1956 char *tmplog;
1957 char *ret;
1958 int iret;
1959 struct logformat_node *tmp;
1960 struct timeval tv;
1961 struct strm_logs tmp_strm_log;
1962
1963 /* FIXME: let's limit ourselves to frontend logging for now. */
1964
1965 if (likely(s)) {
1966 be = s->be;
1967 txn = s->txn;
1968 be_conn = cs_conn(objt_cs(s->si[1].end));
1969 s_flags = s->flags;
1970 uniq_id = s->uniq_id;
1971 logs = &s->logs;
1972 } else {
1973 /* we have no stream so we first need to initialize a few
1974 * things that are needed later. We do increment the request
1975 * ID so that it's uniquely assigned to this request just as
1976 * if the request had reached the point of being processed.
1977 * A request error is reported as it's the only element we have
1978 * here and which justifies emitting such a log.
1979 */
1980 be = fe;
1981 txn = NULL;
1982 be_conn = NULL;
1983 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
1984 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
1985
1986 /* prepare a valid log structure */
1987 tmp_strm_log.tv_accept = sess->tv_accept;
1988 tmp_strm_log.accept_date = sess->accept_date;
1989 tmp_strm_log.t_handshake = sess->t_handshake;
1990 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1991 tv_zero(&tmp_strm_log.tv_request);
1992 tmp_strm_log.t_queue = -1;
1993 tmp_strm_log.t_connect = -1;
1994 tmp_strm_log.t_data = -1;
1995 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1996 tmp_strm_log.bytes_in = 0;
1997 tmp_strm_log.bytes_out = 0;
1998 tmp_strm_log.prx_queue_pos = 0;
1999 tmp_strm_log.srv_queue_pos = 0;
2000
2001 logs = &tmp_strm_log;
2002 }
2003
2004 t_request = -1;
2005 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2006 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
2007
2008 tmplog = dst;
2009
2010 /* fill logbuffer */
2011 if (LIST_ISEMPTY(list_format))
2012 return 0;
2013
2014 list_for_each_entry(tmp, list_format, list) {
2015 struct connection *conn;
2016 const char *src = NULL;
2017 struct sample *key;
2018 const struct buffer empty = { };
2019
2020 switch (tmp->type) {
2021 case LOG_FMT_SEPARATOR:
2022 if (!last_isspace) {
2023 LOGCHAR(' ');
2024 last_isspace = 1;
2025 }
2026 break;
2027
2028 case LOG_FMT_TEXT: // text
2029 src = tmp->arg;
2030 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2031 if (iret == 0)
2032 goto out;
2033 tmplog += iret;
2034 last_isspace = 0;
2035 break;
2036
2037 case LOG_FMT_EXPR: // sample expression, may be request or response
2038 key = NULL;
2039 if (tmp->options & LOG_OPT_REQ_CAP && s)
2040 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
2041 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
2042 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
2043 if (tmp->options & LOG_OPT_HTTP)
2044 ret = lf_encode_chunk(tmplog, dst + maxsize,
2045 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
2046 else
2047 ret = lf_text_len(tmplog,
2048 key ? key->data.u.str.area : NULL,
2049 key ? key->data.u.str.data : 0,
2050 dst + maxsize - tmplog,
2051 tmp);
2052 if (ret == 0)
2053 goto out;
2054 tmplog = ret;
2055 last_isspace = 0;
2056 break;
2057
2058 case LOG_FMT_CLIENTIP: // %ci
2059 conn = objt_conn(sess->origin);
2060 if (conn && conn_get_src(conn))
2061 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
2062 else
2063 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2064 if (ret == NULL)
2065 goto out;
2066 tmplog = ret;
2067 last_isspace = 0;
2068 break;
2069
2070 case LOG_FMT_CLIENTPORT: // %cp
2071 conn = objt_conn(sess->origin);
2072 if (conn && conn_get_src(conn)) {
2073 if (conn->src->ss_family == AF_UNIX) {
2074 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
2075 } else {
2076 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
2077 dst + maxsize - tmplog, tmp);
2078 }
2079 }
2080 else
2081 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2082
2083 if (ret == NULL)
2084 goto out;
2085 tmplog = ret;
2086 last_isspace = 0;
2087 break;
2088
2089 case LOG_FMT_FRONTENDIP: // %fi
2090 conn = objt_conn(sess->origin);
2091 if (conn && conn_get_dst(conn)) {
2092 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
2093 }
2094 else
2095 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2096
2097 if (ret == NULL)
2098 goto out;
2099 tmplog = ret;
2100 last_isspace = 0;
2101 break;
2102
2103 case LOG_FMT_FRONTENDPORT: // %fp
2104 conn = objt_conn(sess->origin);
2105 if (conn && conn_get_dst(conn)) {
2106 if (conn->dst->ss_family == AF_UNIX)
2107 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
2108 else
2109 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
2110 }
2111 else
2112 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2113
2114 if (ret == NULL)
2115 goto out;
2116 tmplog = ret;
2117 last_isspace = 0;
2118 break;
2119
2120 case LOG_FMT_BACKENDIP: // %bi
2121 if (be_conn && conn_get_src(be_conn))
2122 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
2123 else
2124 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2125
2126 if (ret == NULL)
2127 goto out;
2128 tmplog = ret;
2129 last_isspace = 0;
2130 break;
2131
2132 case LOG_FMT_BACKENDPORT: // %bp
2133 if (be_conn && conn_get_src(be_conn))
2134 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
2135 else
2136 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2137
2138 if (ret == NULL)
2139 goto out;
2140 tmplog = ret;
2141 last_isspace = 0;
2142 break;
2143
2144 case LOG_FMT_SERVERIP: // %si
2145 if (be_conn && conn_get_dst(be_conn))
2146 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
2147 else
2148 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2149
2150 if (ret == NULL)
2151 goto out;
2152 tmplog = ret;
2153 last_isspace = 0;
2154 break;
2155
2156 case LOG_FMT_SERVERPORT: // %sp
2157 if (be_conn && conn_get_dst(be_conn))
2158 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
2159 else
2160 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2161
2162 if (ret == NULL)
2163 goto out;
2164 tmplog = ret;
2165 last_isspace = 0;
2166 break;
2167
2168 case LOG_FMT_DATE: // %t = accept date
2169 get_localtime(logs->accept_date.tv_sec, &tm);
2170 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
2171 if (ret == NULL)
2172 goto out;
2173 tmplog = ret;
2174 last_isspace = 0;
2175 break;
2176
2177 case LOG_FMT_tr: // %tr = start of request date
2178 /* Note that the timers are valid if we get here */
2179 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
2180 get_localtime(tv.tv_sec, &tm);
2181 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2182 if (ret == NULL)
2183 goto out;
2184 tmplog = ret;
2185 last_isspace = 0;
2186 break;
2187
2188 case LOG_FMT_DATEGMT: // %T = accept date, GMT
2189 get_gmtime(logs->accept_date.tv_sec, &tm);
2190 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2191 if (ret == NULL)
2192 goto out;
2193 tmplog = ret;
2194 last_isspace = 0;
2195 break;
2196
2197 case LOG_FMT_trg: // %trg = start of request date, GMT
2198 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
2199 get_gmtime(tv.tv_sec, &tm);
2200 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2201 if (ret == NULL)
2202 goto out;
2203 tmplog = ret;
2204 last_isspace = 0;
2205 break;
2206
2207 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
2208 get_localtime(logs->accept_date.tv_sec, &tm);
2209 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
2210 if (ret == NULL)
2211 goto out;
2212 tmplog = ret;
2213 last_isspace = 0;
2214 break;
2215
2216 case LOG_FMT_trl: // %trl = start of request date, local
2217 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
2218 get_localtime(tv.tv_sec, &tm);
2219 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2220 if (ret == NULL)
2221 goto out;
2222 tmplog = ret;
2223 last_isspace = 0;
2224 break;
2225
2226 case LOG_FMT_TS: // %Ts
2227 if (tmp->options & LOG_OPT_HEXA) {
2228 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
2229 if (iret < 0 || iret > dst + maxsize - tmplog)
2230 goto out;
2231 last_isspace = 0;
2232 tmplog += iret;
2233 } else {
2234 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
2235 if (ret == NULL)
2236 goto out;
2237 tmplog = ret;
2238 last_isspace = 0;
2239 }
2240 break;
2241
2242 case LOG_FMT_MS: // %ms
2243 if (tmp->options & LOG_OPT_HEXA) {
2244 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
2245 if (iret < 0 || iret > dst + maxsize - tmplog)
2246 goto out;
2247 last_isspace = 0;
2248 tmplog += iret;
2249 } else {
2250 if ((dst + maxsize - tmplog) < 4)
2251 goto out;
2252 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
2253 tmplog, 4);
2254 if (ret == NULL)
2255 goto out;
2256 tmplog = ret;
2257 last_isspace = 0;
2258 }
2259 break;
2260
2261 case LOG_FMT_FRONTEND: // %f
2262 src = fe->id;
2263 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2264 if (ret == NULL)
2265 goto out;
2266 tmplog = ret;
2267 last_isspace = 0;
2268 break;
2269
2270 case LOG_FMT_FRONTEND_XPRT: // %ft
2271 src = fe->id;
2272 if (tmp->options & LOG_OPT_QUOTE)
2273 LOGCHAR('"');
2274 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2275 if (iret == 0)
2276 goto out;
2277 tmplog += iret;
2278 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
2279 LOGCHAR('~');
2280 if (tmp->options & LOG_OPT_QUOTE)
2281 LOGCHAR('"');
2282 last_isspace = 0;
2283 break;
2284 #ifdef USE_OPENSSL
2285 case LOG_FMT_SSL_CIPHER: // %sslc
2286 src = NULL;
2287 conn = objt_conn(sess->origin);
2288 if (conn) {
2289 src = ssl_sock_get_cipher_name(conn);
2290 }
2291 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2292 if (ret == NULL)
2293 goto out;
2294 tmplog = ret;
2295 last_isspace = 0;
2296 break;
2297
2298 case LOG_FMT_SSL_VERSION: // %sslv
2299 src = NULL;
2300 conn = objt_conn(sess->origin);
2301 if (conn) {
2302 src = ssl_sock_get_proto_version(conn);
2303 }
2304 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2305 if (ret == NULL)
2306 goto out;
2307 tmplog = ret;
2308 last_isspace = 0;
2309 break;
2310 #endif
2311 case LOG_FMT_BACKEND: // %b
2312 src = be->id;
2313 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2314 if (ret == NULL)
2315 goto out;
2316 tmplog = ret;
2317 last_isspace = 0;
2318 break;
2319
2320 case LOG_FMT_SERVER: // %s
2321 switch (obj_type(s ? s->target : NULL)) {
2322 case OBJ_TYPE_SERVER:
2323 src = __objt_server(s->target)->id;
2324 break;
2325 case OBJ_TYPE_APPLET:
2326 src = __objt_applet(s->target)->name;
2327 break;
2328 default:
2329 src = "<NOSRV>";
2330 break;
2331 }
2332 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2333 if (ret == NULL)
2334 goto out;
2335 tmplog = ret;
2336 last_isspace = 0;
2337 break;
2338
2339 case LOG_FMT_Th: // %Th = handshake time
2340 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
2341 if (ret == NULL)
2342 goto out;
2343 tmplog = ret;
2344 last_isspace = 0;
2345 break;
2346
2347 case LOG_FMT_Ti: // %Ti = HTTP idle time
2348 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
2349 if (ret == NULL)
2350 goto out;
2351 tmplog = ret;
2352 last_isspace = 0;
2353 break;
2354
2355 case LOG_FMT_TR: // %TR = HTTP request time
2356 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
2357 tmplog, dst + maxsize - tmplog);
2358 if (ret == NULL)
2359 goto out;
2360 tmplog = ret;
2361 last_isspace = 0;
2362 break;
2363
2364 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
2365 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
2366 if (ret == NULL)
2367 goto out;
2368 tmplog = ret;
2369 last_isspace = 0;
2370 break;
2371
2372 case LOG_FMT_TW: // %Tw
2373 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
2374 tmplog, dst + maxsize - tmplog);
2375 if (ret == NULL)
2376 goto out;
2377 tmplog = ret;
2378 last_isspace = 0;
2379 break;
2380
2381 case LOG_FMT_TC: // %Tc
2382 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
2383 tmplog, dst + maxsize - tmplog);
2384 if (ret == NULL)
2385 goto out;
2386 tmplog = ret;
2387 last_isspace = 0;
2388 break;
2389
2390 case LOG_FMT_Tr: // %Tr
2391 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
2392 tmplog, dst + maxsize - tmplog);
2393 if (ret == NULL)
2394 goto out;
2395 tmplog = ret;
2396 last_isspace = 0;
2397 break;
2398
2399 case LOG_FMT_TD: // %Td
2400 if (be->mode == PR_MODE_HTTP)
2401 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
2402 tmplog, dst + maxsize - tmplog);
2403 else
2404 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
2405 tmplog, dst + maxsize - tmplog);
2406 if (ret == NULL)
2407 goto out;
2408 tmplog = ret;
2409 last_isspace = 0;
2410 break;
2411
2412 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2413 if (!(fe->to_log & LW_BYTES))
2414 LOGCHAR('+');
2415 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0),
2416 tmplog, dst + maxsize - tmplog);
2417 if (ret == NULL)
2418 goto out;
2419 tmplog = ret;
2420 last_isspace = 0;
2421 break;
2422
2423 case LOG_FMT_TT: // %Tt = total time
2424 if (!(fe->to_log & LW_BYTES))
2425 LOGCHAR('+');
2426 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
2427 if (ret == NULL)
2428 goto out;
2429 tmplog = ret;
2430 last_isspace = 0;
2431 break;
2432
2433 case LOG_FMT_STATUS: // %ST
2434 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
2435 if (ret == NULL)
2436 goto out;
2437 tmplog = ret;
2438 last_isspace = 0;
2439 break;
2440
2441 case LOG_FMT_BYTES: // %B
2442 if (!(fe->to_log & LW_BYTES))
2443 LOGCHAR('+');
2444 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
2445 if (ret == NULL)
2446 goto out;
2447 tmplog = ret;
2448 last_isspace = 0;
2449 break;
2450
2451 case LOG_FMT_BYTES_UP: // %U
2452 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
2453 if (ret == NULL)
2454 goto out;
2455 tmplog = ret;
2456 last_isspace = 0;
2457 break;
2458
2459 case LOG_FMT_CCLIENT: // %CC
2460 src = txn ? txn->cli_cookie : NULL;
2461 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2462 if (ret == NULL)
2463 goto out;
2464 tmplog = ret;
2465 last_isspace = 0;
2466 break;
2467
2468 case LOG_FMT_CSERVER: // %CS
2469 src = txn ? txn->srv_cookie : NULL;
2470 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2471 if (ret == NULL)
2472 goto out;
2473 tmplog = ret;
2474 last_isspace = 0;
2475 break;
2476
2477 case LOG_FMT_TERMSTATE: // %ts
2478 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2479 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
2480 *tmplog = '\0';
2481 last_isspace = 0;
2482 break;
2483
2484 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
2485 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2486 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
2487 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2488 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-');
2489 last_isspace = 0;
2490 break;
2491
2492 case LOG_FMT_ACTCONN: // %ac
2493 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
2494 if (ret == NULL)
2495 goto out;
2496 tmplog = ret;
2497 last_isspace = 0;
2498 break;
2499
2500 case LOG_FMT_FECONN: // %fc
2501 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
2502 if (ret == NULL)
2503 goto out;
2504 tmplog = ret;
2505 last_isspace = 0;
2506 break;
2507
2508 case LOG_FMT_BECONN: // %bc
2509 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
2510 if (ret == NULL)
2511 goto out;
2512 tmplog = ret;
2513 last_isspace = 0;
2514 break;
2515
2516 case LOG_FMT_SRVCONN: // %sc
2517 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
2518 objt_server(s->target)->cur_sess :
2519 0, tmplog, dst + maxsize - tmplog);
2520 if (ret == NULL)
2521 goto out;
2522 tmplog = ret;
2523 last_isspace = 0;
2524 break;
2525
2526 case LOG_FMT_RETRIES: // %rq
2527 if (s_flags & SF_REDISP)
2528 LOGCHAR('+');
2529 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
2530 (be->conn_retries - s->si[1].conn_retries) :
2531 be->conn_retries, tmplog, dst + maxsize - tmplog);
2532 if (ret == NULL)
2533 goto out;
2534 tmplog = ret;
2535 last_isspace = 0;
2536 break;
2537
2538 case LOG_FMT_SRVQUEUE: // %sq
2539 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
2540 if (ret == NULL)
2541 goto out;
2542 tmplog = ret;
2543 last_isspace = 0;
2544 break;
2545
2546 case LOG_FMT_BCKQUEUE: // %bq
2547 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
2548 if (ret == NULL)
2549 goto out;
2550 tmplog = ret;
2551 last_isspace = 0;
2552 break;
2553
2554 case LOG_FMT_HDRREQUEST: // %hr
2555 /* request header */
2556 if (fe->nb_req_cap && s && s->req_cap) {
2557 if (tmp->options & LOG_OPT_QUOTE)
2558 LOGCHAR('"');
2559 LOGCHAR('{');
2560 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2561 if (hdr)
2562 LOGCHAR('|');
2563 if (s->req_cap[hdr] != NULL) {
2564 ret = lf_encode_string(tmplog, dst + maxsize,
2565 '#', hdr_encode_map, s->req_cap[hdr], tmp);
2566 if (ret == NULL || *ret != '\0')
2567 goto out;
2568 tmplog = ret;
2569 }
2570 }
2571 LOGCHAR('}');
2572 if (tmp->options & LOG_OPT_QUOTE)
2573 LOGCHAR('"');
2574 last_isspace = 0;
2575 }
2576 break;
2577
2578 case LOG_FMT_HDRREQUESTLIST: // %hrl
2579 /* request header list */
2580 if (fe->nb_req_cap && s && s->req_cap) {
2581 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2582 if (hdr > 0)
2583 LOGCHAR(' ');
2584 if (tmp->options & LOG_OPT_QUOTE)
2585 LOGCHAR('"');
2586 if (s->req_cap[hdr] != NULL) {
2587 ret = lf_encode_string(tmplog, dst + maxsize,
2588 '#', hdr_encode_map, s->req_cap[hdr], tmp);
2589 if (ret == NULL || *ret != '\0')
2590 goto out;
2591 tmplog = ret;
2592 } else if (!(tmp->options & LOG_OPT_QUOTE))
2593 LOGCHAR('-');
2594 if (tmp->options & LOG_OPT_QUOTE)
2595 LOGCHAR('"');
2596 last_isspace = 0;
2597 }
2598 }
2599 break;
2600
2601
2602 case LOG_FMT_HDRRESPONS: // %hs
2603 /* response header */
2604 if (fe->nb_rsp_cap && s && s->res_cap) {
2605 if (tmp->options & LOG_OPT_QUOTE)
2606 LOGCHAR('"');
2607 LOGCHAR('{');
2608 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2609 if (hdr)
2610 LOGCHAR('|');
2611 if (s->res_cap[hdr] != NULL) {
2612 ret = lf_encode_string(tmplog, dst + maxsize,
2613 '#', hdr_encode_map, s->res_cap[hdr], tmp);
2614 if (ret == NULL || *ret != '\0')
2615 goto out;
2616 tmplog = ret;
2617 }
2618 }
2619 LOGCHAR('}');
2620 last_isspace = 0;
2621 if (tmp->options & LOG_OPT_QUOTE)
2622 LOGCHAR('"');
2623 }
2624 break;
2625
2626 case LOG_FMT_HDRRESPONSLIST: // %hsl
2627 /* response header list */
2628 if (fe->nb_rsp_cap && s && s->res_cap) {
2629 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2630 if (hdr > 0)
2631 LOGCHAR(' ');
2632 if (tmp->options & LOG_OPT_QUOTE)
2633 LOGCHAR('"');
2634 if (s->res_cap[hdr] != NULL) {
2635 ret = lf_encode_string(tmplog, dst + maxsize,
2636 '#', hdr_encode_map, s->res_cap[hdr], tmp);
2637 if (ret == NULL || *ret != '\0')
2638 goto out;
2639 tmplog = ret;
2640 } else if (!(tmp->options & LOG_OPT_QUOTE))
2641 LOGCHAR('-');
2642 if (tmp->options & LOG_OPT_QUOTE)
2643 LOGCHAR('"');
2644 last_isspace = 0;
2645 }
2646 }
2647 break;
2648
2649 case LOG_FMT_REQ: // %r
2650 /* Request */
2651 if (tmp->options & LOG_OPT_QUOTE)
2652 LOGCHAR('"');
2653 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2654 ret = lf_encode_string(tmplog, dst + maxsize,
2655 '#', url_encode_map, uri, tmp);
2656 if (ret == NULL || *ret != '\0')
2657 goto out;
2658 tmplog = ret;
2659 if (tmp->options & LOG_OPT_QUOTE)
2660 LOGCHAR('"');
2661 last_isspace = 0;
2662 break;
2663
2664 case LOG_FMT_HTTP_PATH: // %HP
2665 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2666
2667 if (tmp->options & LOG_OPT_QUOTE)
2668 LOGCHAR('"');
2669
2670 end = uri + strlen(uri);
2671 // look for the first whitespace character
2672 while (uri < end && !HTTP_IS_SPHT(*uri))
2673 uri++;
2674
2675 // keep advancing past multiple spaces
2676 while (uri < end && HTTP_IS_SPHT(*uri)) {
2677 uri++; nspaces++;
2678 }
2679
2680 // look for first space or question mark after url
2681 spc = uri;
2682 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2683 spc++;
2684
2685 if (!txn || !txn->uri || nspaces == 0) {
2686 chunk.area = "<BADREQ>";
2687 chunk.data = strlen("<BADREQ>");
2688 } else {
2689 chunk.area = uri;
2690 chunk.data = spc - uri;
2691 }
2692
2693 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2694 if (ret == NULL || *ret != '\0')
2695 goto out;
2696
2697 tmplog = ret;
2698 if (tmp->options & LOG_OPT_QUOTE)
2699 LOGCHAR('"');
2700
2701 last_isspace = 0;
2702 break;
2703
2704 case LOG_FMT_HTTP_QUERY: // %HQ
2705 if (tmp->options & LOG_OPT_QUOTE)
2706 LOGCHAR('"');
2707
2708 if (!txn || !txn->uri) {
2709 chunk.area = "<BADREQ>";
2710 chunk.data = strlen("<BADREQ>");
2711 } else {
2712 uri = txn->uri;
2713 end = uri + strlen(uri);
2714 // look for the first question mark
2715 while (uri < end && *uri != '?')
2716 uri++;
2717
2718 qmark = uri;
2719 // look for first space or question mark after url
2720 while (uri < end && !HTTP_IS_SPHT(*uri))
2721 uri++;
2722
2723 chunk.area = qmark;
2724 chunk.data = uri - qmark;
2725 }
2726
2727 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2728 if (ret == NULL || *ret != '\0')
2729 goto out;
2730
2731 tmplog = ret;
2732 if (tmp->options & LOG_OPT_QUOTE)
2733 LOGCHAR('"');
2734
2735 last_isspace = 0;
2736 break;
2737
2738 case LOG_FMT_HTTP_URI: // %HU
2739 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2740
2741 if (tmp->options & LOG_OPT_QUOTE)
2742 LOGCHAR('"');
2743
2744 end = uri + strlen(uri);
2745 // look for the first whitespace character
2746 while (uri < end && !HTTP_IS_SPHT(*uri))
2747 uri++;
2748
2749 // keep advancing past multiple spaces
2750 while (uri < end && HTTP_IS_SPHT(*uri)) {
2751 uri++; nspaces++;
2752 }
2753
2754 // look for first space after url
2755 spc = uri;
2756 while (spc < end && !HTTP_IS_SPHT(*spc))
2757 spc++;
2758
2759 if (!txn || !txn->uri || nspaces == 0) {
2760 chunk.area = "<BADREQ>";
2761 chunk.data = strlen("<BADREQ>");
2762 } else {
2763 chunk.area = uri;
2764 chunk.data = spc - uri;
2765 }
2766
2767 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2768 if (ret == NULL || *ret != '\0')
2769 goto out;
2770
2771 tmplog = ret;
2772 if (tmp->options & LOG_OPT_QUOTE)
2773 LOGCHAR('"');
2774
2775 last_isspace = 0;
2776 break;
2777
2778 case LOG_FMT_HTTP_METHOD: // %HM
2779 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2780 if (tmp->options & LOG_OPT_QUOTE)
2781 LOGCHAR('"');
2782
2783 end = uri + strlen(uri);
2784 // look for the first whitespace character
2785 spc = uri;
2786 while (spc < end && !HTTP_IS_SPHT(*spc))
2787 spc++;
2788
2789 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
2790 chunk.area = "<BADREQ>";
2791 chunk.data = strlen("<BADREQ>");
2792 } else {
2793 chunk.area = uri;
2794 chunk.data = spc - uri;
2795 }
2796
2797 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2798 if (ret == NULL || *ret != '\0')
2799 goto out;
2800
2801 tmplog = ret;
2802 if (tmp->options & LOG_OPT_QUOTE)
2803 LOGCHAR('"');
2804
2805 last_isspace = 0;
2806 break;
2807
2808 case LOG_FMT_HTTP_VERSION: // %HV
2809 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2810 if (tmp->options & LOG_OPT_QUOTE)
2811 LOGCHAR('"');
2812
2813 end = uri + strlen(uri);
2814 // look for the first whitespace character
2815 while (uri < end && !HTTP_IS_SPHT(*uri))
2816 uri++;
2817
2818 // keep advancing past multiple spaces
2819 while (uri < end && HTTP_IS_SPHT(*uri)) {
2820 uri++; nspaces++;
2821 }
2822
2823 // look for the next whitespace character
2824 while (uri < end && !HTTP_IS_SPHT(*uri))
2825 uri++;
2826
2827 // keep advancing past multiple spaces
2828 while (uri < end && HTTP_IS_SPHT(*uri))
2829 uri++;
2830
2831 if (!txn || !txn->uri || nspaces == 0) {
2832 chunk.area = "<BADREQ>";
2833 chunk.data = strlen("<BADREQ>");
2834 } else if (uri == end) {
2835 chunk.area = "HTTP/0.9";
2836 chunk.data = strlen("HTTP/0.9");
2837 } else {
2838 chunk.area = uri;
2839 chunk.data = end - uri;
2840 }
2841
2842 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2843 if (ret == NULL || *ret != '\0')
2844 goto out;
2845
2846 tmplog = ret;
2847 if (tmp->options & LOG_OPT_QUOTE)
2848 LOGCHAR('"');
2849
2850 last_isspace = 0;
2851 break;
2852
2853 case LOG_FMT_COUNTER: // %rt
2854 if (tmp->options & LOG_OPT_HEXA) {
2855 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
2856 if (iret < 0 || iret > dst + maxsize - tmplog)
2857 goto out;
2858 last_isspace = 0;
2859 tmplog += iret;
2860 } else {
2861 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
2862 if (ret == NULL)
2863 goto out;
2864 tmplog = ret;
2865 last_isspace = 0;
2866 }
2867 break;
2868
2869 case LOG_FMT_LOGCNT: // %lc
2870 if (tmp->options & LOG_OPT_HEXA) {
2871 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
2872 if (iret < 0 || iret > dst + maxsize - tmplog)
2873 goto out;
2874 last_isspace = 0;
2875 tmplog += iret;
2876 } else {
2877 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
2878 if (ret == NULL)
2879 goto out;
2880 tmplog = ret;
2881 last_isspace = 0;
2882 }
2883 break;
2884
2885 case LOG_FMT_HOSTNAME: // %H
2886 src = hostname;
2887 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2888 if (ret == NULL)
2889 goto out;
2890 tmplog = ret;
2891 last_isspace = 0;
2892 break;
2893
2894 case LOG_FMT_PID: // %pid
2895 if (tmp->options & LOG_OPT_HEXA) {
2896 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2897 if (iret < 0 || iret > dst + maxsize - tmplog)
2898 goto out;
2899 last_isspace = 0;
2900 tmplog += iret;
2901 } else {
2902 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2903 if (ret == NULL)
2904 goto out;
2905 tmplog = ret;
2906 last_isspace = 0;
2907 }
2908 break;
2909
2910 case LOG_FMT_UNIQUEID: // %ID
2911 ret = NULL;
2912 src = s ? s->unique_id : NULL;
2913 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
2914 if (ret == NULL)
2915 goto out;
2916 tmplog = ret;
2917 last_isspace = 0;
2918 break;
2919
2920 }
2921 }
2922
2923 out:
2924 /* *tmplog is a unused character */
2925 *tmplog = '\0';
2926 return tmplog - dst;
2927
2928 }
2929
2930 /*
2931 * send a log for the stream when we have enough info about it.
2932 * Will not log if the frontend has no log defined.
2933 */
strm_log(struct stream * s)2934 void strm_log(struct stream *s)
2935 {
2936 struct session *sess = s->sess;
2937 int size, err, level;
2938 int sd_size = 0;
2939
2940 /* if we don't want to log normal traffic, return now */
2941 err = (s->flags & SF_REDISP) ||
2942 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2943 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
2944 (s->si[1].conn_retries != s->be->conn_retries)) ||
2945 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
2946
2947 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
2948 return;
2949
2950 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2951 return;
2952
2953 if (s->logs.level) { /* loglevel was overridden */
2954 if (s->logs.level == -1) {
2955 s->logs.logwait = 0; /* logs disabled */
2956 return;
2957 }
2958 level = s->logs.level - 1;
2959 }
2960 else {
2961 level = LOG_INFO;
2962 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
2963 level = LOG_ERR;
2964 }
2965
2966 /* if unique-id was not generated */
2967 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
2968 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
2969 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
2970 }
2971
2972 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2973 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2974 &sess->fe->logformat_sd);
2975 }
2976
2977 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
2978 if (size > 0) {
2979 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
2980 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
2981 logline, size + 1, logline_rfc5424, sd_size);
2982 s->logs.logwait = 0;
2983 }
2984 }
2985
2986 /*
2987 * send a minimalist log for the session. Will not log if the frontend has no
2988 * log defined. It is assumed that this is only used to report anomalies that
2989 * cannot lead to the creation of a regular stream. Because of this the log
2990 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2991 * in the frontend. The caller must simply know that it should not call this
2992 * function to report unimportant events. It is safe to call this function with
2993 * sess==NULL (will not do anything).
2994 */
sess_log(struct session * sess)2995 void sess_log(struct session *sess)
2996 {
2997 int size, level;
2998 int sd_size = 0;
2999
3000 if (!sess)
3001 return;
3002
3003 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3004 return;
3005
3006 level = LOG_INFO;
3007 if (sess->fe->options2 & PR_O2_LOGERRORS)
3008 level = LOG_ERR;
3009
3010 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3011 sd_size = sess_build_logline(sess, NULL,
3012 logline_rfc5424, global.max_syslog_len,
3013 &sess->fe->logformat_sd);
3014 }
3015
3016 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
3017 if (size > 0) {
3018 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
3019 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3020 logline, size + 1, logline_rfc5424, sd_size);
3021 }
3022 }
3023
app_log(struct list * logsrvs,struct buffer * tag,int level,const char * format,...)3024 void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3025 {
3026 va_list argp;
3027 int data_len;
3028
3029 if (level < 0 || format == NULL || logline == NULL)
3030 return;
3031
3032 va_start(argp, format);
3033 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3034 if (data_len < 0 || data_len > global.max_syslog_len)
3035 data_len = global.max_syslog_len;
3036 va_end(argp);
3037
3038 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3039 }
3040
3041 /* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
cli_parse_show_startup_logs(char ** args,char * payload,struct appctx * appctx,void * private)3042 static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
3043 {
3044 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
3045 return 1;
3046
3047 if (!startup_logs)
3048 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
3049
3050 return ring_attach_cli(startup_logs, appctx);
3051 }
3052
3053 /* register cli keywords */
3054 static struct cli_kw_list cli_kws = {{ },{
3055 { { "show", "startup-logs", NULL },
3056 "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
3057 {{},}
3058 }};
3059
3060 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3061
3062 REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3063 REGISTER_PER_THREAD_FREE(deinit_log_buffers);
3064
3065 /*
3066 * Local variables:
3067 * c-indent-level: 8
3068 * c-basic-offset: 8
3069 * End:
3070 */
3071