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 smp_log_range *smp_rgs = NULL;
798 struct sockaddr_storage *sk;
799 struct logsrv *logsrv = NULL;
800 int port1, port2;
801 int cur_arg;
802
803 /*
804 * "no log": delete previous herited or defined syslog
805 * servers.
806 */
807 if (do_del) {
808 struct logsrv *back;
809
810 if (*(args[1]) != 0) {
811 memprintf(err, "'no log' does not expect arguments");
812 goto error;
813 }
814
815 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
816 LIST_DEL(&logsrv->list);
817 free(logsrv);
818 }
819 return 1;
820 }
821
822 /*
823 * "log global": copy global.logrsvs linked list to the end of logsrvs
824 * list. But first, we check (logsrvs != global.logsrvs).
825 */
826 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
827 if (logsrvs == &global.logsrvs) {
828 memprintf(err, "'global' is not supported for a global syslog server");
829 goto error;
830 }
831 list_for_each_entry(logsrv, &global.logsrvs, list) {
832 struct logsrv *node;
833
834 list_for_each_entry(node, logsrvs, list) {
835 if (node->ref == logsrv)
836 goto skip_logsrv;
837 }
838
839 node = malloc(sizeof(*node));
840 memcpy(node, logsrv, sizeof(struct logsrv));
841 node->ref = logsrv;
842 LIST_INIT(&node->list);
843 LIST_ADDQ(logsrvs, &node->list);
844
845 skip_logsrv:
846 continue;
847 }
848 return 1;
849 }
850
851 /*
852 * "log <address> ...: parse a syslog server line
853 */
854 if (*(args[1]) == 0 || *(args[2]) == 0) {
855 memprintf(err, "expects <address> and <facility> %s as arguments",
856 ((logsrvs == &global.logsrvs) ? "" : "or global"));
857 goto error;
858 }
859
860 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
861 if (strcmp(args[1], "stdout") == 0)
862 args[1] = "fd@1";
863 else if (strcmp(args[1], "stderr") == 0)
864 args[1] = "fd@2";
865
866 logsrv = calloc(1, sizeof(*logsrv));
867 if (!logsrv) {
868 memprintf(err, "out of memory");
869 goto error;
870 }
871
872 /* skip address for now, it will be parsed at the end */
873 cur_arg = 2;
874
875 /* just after the address, a length may be specified */
876 logsrv->maxlen = MAX_SYSLOG_LEN;
877 if (strcmp(args[cur_arg], "len") == 0) {
878 int len = atoi(args[cur_arg+1]);
879 if (len < 80 || len > 65535) {
880 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
881 args[cur_arg+1]);
882 goto error;
883 }
884 logsrv->maxlen = len;
885 cur_arg += 2;
886 }
887 if (logsrv->maxlen > global.max_syslog_len)
888 global.max_syslog_len = logsrv->maxlen;
889
890 /* after the length, a format may be specified */
891 if (strcmp(args[cur_arg], "format") == 0) {
892 logsrv->format = get_log_format(args[cur_arg+1]);
893 if (logsrv->format < 0) {
894 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
895 goto error;
896 }
897 cur_arg += 2;
898 }
899
900 if (strcmp(args[cur_arg], "sample") == 0) {
901 unsigned low, high;
902 char *p, *beg, *end, *smp_sz_str;
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(smp_rgs);
1045 free(logsrv);
1046 return 0;
1047 }
1048
1049
1050 /* Generic function to display messages prefixed by a label */
print_message(const char * label,const char * fmt,va_list argp)1051 static void print_message(const char *label, const char *fmt, va_list argp)
1052 {
1053 struct tm tm;
1054 char *head, *msg;
1055
1056 head = msg = NULL;
1057
1058 get_localtime(date.tv_sec, &tm);
1059 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1060 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1061 memvprintf(&msg, fmt, argp);
1062
1063 if (global.mode & MODE_STARTING) {
1064 if (unlikely(!startup_logs))
1065 startup_logs = ring_new(STARTUP_LOG_SIZE);
1066
1067 if (likely(startup_logs)) {
1068 struct ist m[2];
1069
1070 m[0] = ist(head);
1071 m[1] = ist(msg);
1072 /* trim the trailing '\n' */
1073 if (m[1].len > 0 && m[1].ptr[m[1].len - 1] == '\n')
1074 m[1].len--;
1075 ring_write(startup_logs, ~0, 0, 0, m, 2);
1076 }
1077 }
1078
1079 fprintf(stderr, "%s%s", head, msg);
1080 fflush(stderr);
1081
1082 free(head);
1083 free(msg);
1084 }
1085
1086 /*
1087 * Displays the message on stderr with the date and pid. Overrides the quiet
1088 * mode during startup.
1089 */
ha_alert(const char * fmt,...)1090 void ha_alert(const char *fmt, ...)
1091 {
1092 va_list argp;
1093
1094 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1095 va_start(argp, fmt);
1096 print_message("ALERT", fmt, argp);
1097 va_end(argp);
1098 }
1099 }
1100
1101
1102 /*
1103 * Displays the message on stderr with the date and pid.
1104 */
ha_warning(const char * fmt,...)1105 void ha_warning(const char *fmt, ...)
1106 {
1107 va_list argp;
1108
1109 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1110 va_start(argp, fmt);
1111 print_message("WARNING", fmt, argp);
1112 va_end(argp);
1113 }
1114 }
1115
1116 /*
1117 * Displays the message on stderr with the date and pid.
1118 */
ha_notice(const char * fmt,...)1119 void ha_notice(const char *fmt, ...)
1120 {
1121 va_list argp;
1122
1123 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1124 va_start(argp, fmt);
1125 print_message("NOTICE", fmt, argp);
1126 va_end(argp);
1127 }
1128 }
1129
1130 /*
1131 * Displays the message on <out> only if quiet mode is not set.
1132 */
qfprintf(FILE * out,const char * fmt,...)1133 void qfprintf(FILE *out, const char *fmt, ...)
1134 {
1135 va_list argp;
1136
1137 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1138 va_start(argp, fmt);
1139 vfprintf(out, fmt, argp);
1140 fflush(out);
1141 va_end(argp);
1142 }
1143 }
1144
1145 /*
1146 * returns log format for <fmt> or -1 if not found.
1147 */
get_log_format(const char * fmt)1148 int get_log_format(const char *fmt)
1149 {
1150 int format;
1151
1152 format = LOG_FORMATS - 1;
1153 while (format >= 0 && strcmp(log_formats[format].name, fmt))
1154 format--;
1155
1156 return format;
1157 }
1158
1159 /*
1160 * returns log level for <lev> or -1 if not found.
1161 */
get_log_level(const char * lev)1162 int get_log_level(const char *lev)
1163 {
1164 int level;
1165
1166 level = NB_LOG_LEVELS - 1;
1167 while (level >= 0 && strcmp(log_levels[level], lev))
1168 level--;
1169
1170 return level;
1171 }
1172
1173 /*
1174 * returns log facility for <fac> or -1 if not found.
1175 */
get_log_facility(const char * fac)1176 int get_log_facility(const char *fac)
1177 {
1178 int facility;
1179
1180 facility = NB_LOG_FACILITIES - 1;
1181 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1182 facility--;
1183
1184 return facility;
1185 }
1186
1187 /*
1188 * Encode the string.
1189 *
1190 * When using the +E log format option, it will try to escape '"\]'
1191 * characters with '\' as prefix. The same prefix should not be used as
1192 * <escape>.
1193 */
lf_encode_string(char * start,char * stop,const char escape,const long * map,const char * string,struct logformat_node * node)1194 static char *lf_encode_string(char *start, char *stop,
1195 const char escape, const long *map,
1196 const char *string,
1197 struct logformat_node *node)
1198 {
1199 if (node->options & LOG_OPT_ESC) {
1200 if (start < stop) {
1201 stop--; /* reserve one byte for the final '\0' */
1202 while (start < stop && *string != '\0') {
1203 if (!ha_bit_test((unsigned char)(*string), map)) {
1204 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
1205 *start++ = *string;
1206 else {
1207 if (start + 2 >= stop)
1208 break;
1209 *start++ = '\\';
1210 *start++ = *string;
1211 }
1212 }
1213 else {
1214 if (start + 3 >= stop)
1215 break;
1216 *start++ = escape;
1217 *start++ = hextab[(*string >> 4) & 15];
1218 *start++ = hextab[*string & 15];
1219 }
1220 string++;
1221 }
1222 *start = '\0';
1223 }
1224 }
1225 else {
1226 return encode_string(start, stop, escape, map, string);
1227 }
1228
1229 return start;
1230 }
1231
1232 /*
1233 * Encode the chunk.
1234 *
1235 * When using the +E log format option, it will try to escape '"\]'
1236 * characters with '\' as prefix. The same prefix should not be used as
1237 * <escape>.
1238 */
lf_encode_chunk(char * start,char * stop,const char escape,const long * map,const struct buffer * chunk,struct logformat_node * node)1239 static char *lf_encode_chunk(char *start, char *stop,
1240 const char escape, const long *map,
1241 const struct buffer *chunk,
1242 struct logformat_node *node)
1243 {
1244 char *str, *end;
1245
1246 if (node->options & LOG_OPT_ESC) {
1247 if (start < stop) {
1248 str = chunk->area;
1249 end = chunk->area + chunk->data;
1250
1251 stop--; /* reserve one byte for the final '\0' */
1252 while (start < stop && str < end) {
1253 if (!ha_bit_test((unsigned char)(*str), map)) {
1254 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
1255 *start++ = *str;
1256 else {
1257 if (start + 2 >= stop)
1258 break;
1259 *start++ = '\\';
1260 *start++ = *str;
1261 }
1262 }
1263 else {
1264 if (start + 3 >= stop)
1265 break;
1266 *start++ = escape;
1267 *start++ = hextab[(*str >> 4) & 15];
1268 *start++ = hextab[*str & 15];
1269 }
1270 str++;
1271 }
1272 *start = '\0';
1273 }
1274 }
1275 else {
1276 return encode_chunk(start, stop, escape, map, chunk);
1277 }
1278
1279 return start;
1280 }
1281
1282 /*
1283 * Write a string in the log string
1284 * Take cares of quote and escape options
1285 *
1286 * Return the address of the \0 character, or NULL on error
1287 */
lf_text_len(char * dst,const char * src,size_t len,size_t size,const struct logformat_node * node)1288 char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const struct logformat_node *node)
1289 {
1290 if (size < 2)
1291 return NULL;
1292
1293 if (node->options & LOG_OPT_QUOTE) {
1294 *(dst++) = '"';
1295 size--;
1296 }
1297
1298 if (src && len) {
1299 if (++len > size)
1300 len = size;
1301 if (node->options & LOG_OPT_ESC) {
1302 char *ret;
1303
1304 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
1305 if (ret == NULL || *ret != '\0')
1306 return NULL;
1307 len = ret - dst;
1308 }
1309 else {
1310 len = strlcpy2(dst, src, len);
1311 }
1312
1313 size -= len;
1314 dst += len;
1315 }
1316 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1317 if (size < 2)
1318 return NULL;
1319 *(dst++) = '-';
1320 }
1321
1322 if (node->options & LOG_OPT_QUOTE) {
1323 if (size < 2)
1324 return NULL;
1325 *(dst++) = '"';
1326 }
1327
1328 *dst = '\0';
1329 return dst;
1330 }
1331
lf_text(char * dst,const char * src,size_t size,const struct logformat_node * node)1332 static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
1333 {
1334 return lf_text_len(dst, src, size, size, node);
1335 }
1336
1337 /*
1338 * Write a IP address to the log string
1339 * +X option write in hexadecimal notation, most signifant byte on the left
1340 */
lf_ip(char * dst,const struct sockaddr * sockaddr,size_t size,const struct logformat_node * node)1341 char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
1342 {
1343 char *ret = dst;
1344 int iret;
1345 char pn[INET6_ADDRSTRLEN];
1346
1347 if (node->options & LOG_OPT_HEXA) {
1348 unsigned char *addr = NULL;
1349 switch (sockaddr->sa_family) {
1350 case AF_INET:
1351 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1352 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1353 break;
1354 case AF_INET6:
1355 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1356 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1357 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1358 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1359 break;
1360 default:
1361 return NULL;
1362 }
1363 if (iret < 0 || iret > size)
1364 return NULL;
1365 ret += iret;
1366 } else {
1367 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1368 ret = lf_text(dst, pn, size, node);
1369 if (ret == NULL)
1370 return NULL;
1371 }
1372 return ret;
1373 }
1374
1375 /*
1376 * Write a port to the log
1377 * +X option write in hexadecimal notation, most signifant byte on the left
1378 */
lf_port(char * dst,const struct sockaddr * sockaddr,size_t size,const struct logformat_node * node)1379 char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
1380 {
1381 char *ret = dst;
1382 int iret;
1383
1384 if (node->options & LOG_OPT_HEXA) {
1385 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1386 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1387 if (iret < 0 || iret > size)
1388 return NULL;
1389 ret += iret;
1390 } else {
1391 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1392 if (ret == NULL)
1393 return NULL;
1394 }
1395 return ret;
1396 }
1397
1398 /* Re-generate time-based part of the syslog header in RFC3164 format at
1399 * the beginning of logheader once a second and return the pointer to the
1400 * first character after it.
1401 */
update_log_hdr(const time_t time)1402 static char *update_log_hdr(const time_t time)
1403 {
1404 static THREAD_LOCAL long tvsec;
1405 static THREAD_LOCAL struct buffer host = { };
1406 static THREAD_LOCAL int sep = 0;
1407
1408 if (unlikely(time != tvsec || logheader_end == NULL)) {
1409 /* this string is rebuild only once a second */
1410 struct tm tm;
1411 int hdr_len;
1412
1413 tvsec = time;
1414 get_localtime(tvsec, &tm);
1415
1416 if (unlikely(global.log_send_hostname != host.area)) {
1417 host.area = global.log_send_hostname;
1418 host.data = host.area ? strlen(host.area) : 0;
1419 sep = host.data ? 1 : 0;
1420 }
1421
1422 hdr_len = snprintf(logheader, global.max_syslog_len,
1423 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
1424 monthname[tm.tm_mon],
1425 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
1426 (int)host.data, host.area, sep, "");
1427 /* WARNING: depending upon implementations, snprintf may return
1428 * either -1 or the number of bytes that would be needed to store
1429 * the total message. In both cases, we must adjust it.
1430 */
1431 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1432 hdr_len = global.max_syslog_len;
1433
1434 logheader_end = logheader + hdr_len;
1435 }
1436
1437 logheader_end[0] = 0; // ensure we get rid of any previous attempt
1438
1439 return logheader_end;
1440 }
1441
1442 /* Re-generate time-based part of the syslog header in RFC5424 format at
1443 * the beginning of logheader_rfc5424 once a second and return the pointer
1444 * to the first character after it.
1445 */
update_log_hdr_rfc5424(const time_t time)1446 static char *update_log_hdr_rfc5424(const time_t time)
1447 {
1448 static THREAD_LOCAL long tvsec;
1449 const char *gmt_offset;
1450
1451 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
1452 /* this string is rebuild only once a second */
1453 struct tm tm;
1454 int hdr_len;
1455
1456 tvsec = time;
1457 get_localtime(tvsec, &tm);
1458 gmt_offset = get_gmt_offset(time, &tm);
1459
1460 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
1461 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
1462 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
1463 tm.tm_hour, tm.tm_min, tm.tm_sec,
1464 gmt_offset, gmt_offset+3,
1465 global.log_send_hostname ? global.log_send_hostname : hostname);
1466 /* WARNING: depending upon implementations, snprintf may return
1467 * either -1 or the number of bytes that would be needed to store
1468 * the total message. In both cases, we must adjust it.
1469 */
1470 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1471 hdr_len = global.max_syslog_len;
1472
1473 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
1474 }
1475
1476 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
1477
1478 return logheader_rfc5424_end;
1479 }
1480
1481 /*
1482 * This function sends the syslog message using a printf format string. It
1483 * expects an LF-terminated message.
1484 */
send_log(struct proxy * p,int level,const char * format,...)1485 void send_log(struct proxy *p, int level, const char *format, ...)
1486 {
1487 va_list argp;
1488 int data_len;
1489
1490 if (level < 0 || format == NULL || logline == NULL)
1491 return;
1492
1493 va_start(argp, format);
1494 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
1495 if (data_len < 0 || data_len > global.max_syslog_len)
1496 data_len = global.max_syslog_len;
1497 va_end(argp);
1498
1499 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1500 logline, data_len, default_rfc5424_sd_log_format, 2);
1501 }
1502
1503 /*
1504 * This function sends a syslog message to <logsrv>.
1505 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1506 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1507 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
1508 * It overrides the last byte of the message vector with an LF character.
1509 * Does not return any error,
1510 */
__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)1511 static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1512 int level, char *message, size_t size, char *sd, size_t sd_size,
1513 char *tag_str, size_t tag_size)
1514 {
1515 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1516 static THREAD_LOCAL struct msghdr msghdr = {
1517 //.msg_iov = iovec,
1518 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1519 };
1520 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1521 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1522 static THREAD_LOCAL char *dataptr = NULL;
1523 time_t time = date.tv_sec;
1524 char *hdr, *hdr_ptr;
1525 size_t hdr_size;
1526 int fac_level;
1527 int *plogfd;
1528 char *pid_sep1 = "", *pid_sep2 = "";
1529 char logheader_short[3];
1530 int sent;
1531 int maxlen;
1532 int hdr_max = 0;
1533 int tag_max = 0;
1534 int pid_sep1_max = 0;
1535 int pid_max = 0;
1536 int pid_sep2_max = 0;
1537 int sd_max = 0;
1538 int max = 0;
1539
1540 msghdr.msg_iov = iovec;
1541
1542 dataptr = message;
1543
1544 /* historically some messages used to already contain the trailing LF
1545 * or Zero. Let's remove all trailing LF or Zero
1546 */
1547 while (size && ((dataptr[size-1] == '\n' || (dataptr[size-1] == 0))))
1548 size--;
1549
1550 if (logsrv->type == LOG_TARGET_FD) {
1551 /* the socket's address is a file descriptor */
1552 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1553 }
1554 else if (logsrv->type == LOG_TARGET_BUFFER) {
1555 plogfd = NULL;
1556 }
1557 else if (logsrv->addr.ss_family == AF_UNIX)
1558 plogfd = &logfdunix;
1559 else
1560 plogfd = &logfdinet;
1561
1562 if (plogfd && unlikely(*plogfd < 0)) {
1563 /* socket not successfully initialized yet */
1564 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1565 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1566 static char once;
1567
1568 if (!once) {
1569 once = 1; /* note: no need for atomic ops here */
1570 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1571 nblogger, strerror(errno), errno);
1572 }
1573 return;
1574 } else {
1575 /* we don't want to receive anything on this socket */
1576 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1577 /* does nothing under Linux, maybe needed for others */
1578 shutdown(*plogfd, SHUT_RD);
1579 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1580 }
1581 }
1582
1583 switch (logsrv->format) {
1584 case LOG_FORMAT_RFC3164:
1585 hdr = logheader;
1586 hdr_ptr = update_log_hdr(time);
1587 break;
1588
1589 case LOG_FORMAT_RFC5424:
1590 hdr = logheader_rfc5424;
1591 hdr_ptr = update_log_hdr_rfc5424(time);
1592 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1593 break;
1594
1595 case LOG_FORMAT_SHORT:
1596 /* all fields are known, skip the header generation */
1597 hdr = logheader_short;
1598 hdr[0] = '<';
1599 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1600 hdr[2] = '>';
1601 hdr_ptr = hdr;
1602 hdr_max = 3;
1603 maxlen = logsrv->maxlen - hdr_max;
1604 max = MIN(size, maxlen - 1);
1605 goto send;
1606
1607 case LOG_FORMAT_RAW:
1608 /* all fields are known, skip the header generation */
1609 hdr_ptr = hdr = "";
1610 hdr_max = 0;
1611 maxlen = logsrv->maxlen;
1612 max = MIN(size, maxlen - 1);
1613 goto send;
1614
1615 default:
1616 return; /* must never happen */
1617 }
1618
1619 hdr_size = hdr_ptr - hdr;
1620
1621 /* For each target, we may have a different facility.
1622 * We can also have a different log level for each message.
1623 * This induces variations in the message header length.
1624 * Since we don't want to recompute it each time, nor copy it every
1625 * time, we only change the facility in the pre-computed header,
1626 * and we change the pointer to the header accordingly.
1627 */
1628 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1629 hdr_ptr = hdr + 3; /* last digit of the log level */
1630 do {
1631 *hdr_ptr = '0' + fac_level % 10;
1632 fac_level /= 10;
1633 hdr_ptr--;
1634 } while (fac_level && hdr_ptr > hdr);
1635 *hdr_ptr = '<';
1636
1637 hdr_max = hdr_size - (hdr_ptr - hdr);
1638
1639 /* time-based header */
1640 if (unlikely(hdr_size >= logsrv->maxlen)) {
1641 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1642 sd_max = 0;
1643 goto send;
1644 }
1645
1646 maxlen = logsrv->maxlen - hdr_max;
1647
1648 /* tag */
1649 tag_max = tag_size;
1650 if (unlikely(tag_max >= maxlen)) {
1651 tag_max = maxlen - 1;
1652 sd_max = 0;
1653 goto send;
1654 }
1655
1656 maxlen -= tag_max;
1657
1658 /* first pid separator */
1659 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1660 if (unlikely(pid_sep1_max >= maxlen)) {
1661 pid_sep1_max = maxlen - 1;
1662 sd_max = 0;
1663 goto send;
1664 }
1665
1666 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1667 maxlen -= pid_sep1_max;
1668
1669 /* pid */
1670 pid_max = pid_size;
1671 if (unlikely(pid_size >= maxlen)) {
1672 pid_size = maxlen - 1;
1673 sd_max = 0;
1674 goto send;
1675 }
1676
1677 maxlen -= pid_size;
1678
1679 /* second pid separator */
1680 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1681 if (unlikely(pid_sep2_max >= maxlen)) {
1682 pid_sep2_max = maxlen - 1;
1683 sd_max = 0;
1684 goto send;
1685 }
1686
1687 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1688 maxlen -= pid_sep2_max;
1689
1690 /* structured-data */
1691 if (sd_max >= maxlen) {
1692 sd_max = maxlen - 1;
1693 goto send;
1694 }
1695
1696 max = MIN(size, maxlen - sd_max - 1);
1697 send:
1698 if (logsrv->addr.ss_family == AF_UNSPEC) {
1699 /* the target is a file descriptor or a ring buffer */
1700 struct ist msg[7];
1701
1702 msg[0].ptr = hdr_ptr; msg[0].len = hdr_max;
1703 msg[1].ptr = tag_str; msg[1].len = tag_max;
1704 msg[2].ptr = pid_sep1; msg[2].len = pid_sep1_max;
1705 msg[3].ptr = pid_str; msg[3].len = pid_max;
1706 msg[4].ptr = pid_sep2; msg[4].len = pid_sep2_max;
1707 msg[5].ptr = sd; msg[5].len = sd_max;
1708 msg[6].ptr = dataptr; msg[6].len = max;
1709
1710 if (logsrv->type == LOG_TARGET_BUFFER)
1711 sent = ring_write(logsrv->ring, ~0, NULL, 0, msg, 7);
1712 else /* LOG_TARGET_FD */
1713 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
1714 }
1715 else {
1716 iovec[0].iov_base = hdr_ptr;
1717 iovec[0].iov_len = hdr_max;
1718 iovec[1].iov_base = tag_str;
1719 iovec[1].iov_len = tag_max;
1720 iovec[2].iov_base = pid_sep1;
1721 iovec[2].iov_len = pid_sep1_max;
1722 iovec[3].iov_base = pid_str;
1723 iovec[3].iov_len = pid_max;
1724 iovec[4].iov_base = pid_sep2;
1725 iovec[4].iov_len = pid_sep2_max;
1726 iovec[5].iov_base = sd;
1727 iovec[5].iov_len = sd_max;
1728 iovec[6].iov_base = dataptr;
1729 iovec[6].iov_len = max;
1730 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1731 iovec[7].iov_len = 1;
1732
1733 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1734 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
1735
1736 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1737 }
1738
1739 if (sent < 0) {
1740 static char once;
1741
1742 if (errno == EAGAIN)
1743 _HA_ATOMIC_ADD(&dropped_logs, 1);
1744 else if (!once) {
1745 once = 1; /* note: no need for atomic ops here */
1746 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1747 nblogger, strerror(errno), errno);
1748 }
1749 }
1750 }
1751
1752 /*
1753 * This function sends a syslog message.
1754 * It doesn't care about errors nor does it report them.
1755 * The arguments <sd> and <sd_size> are used for the structured-data part
1756 * in RFC5424 formatted syslog messages.
1757 */
__send_log(struct list * logsrvs,struct buffer * tag,int level,char * message,size_t size,char * sd,size_t sd_size)1758 void __send_log(struct list *logsrvs, struct buffer *tag, int level,
1759 char *message, size_t size, char *sd, size_t sd_size)
1760 {
1761 struct logsrv *logsrv;
1762 int nblogger;
1763 static THREAD_LOCAL int curr_pid;
1764 static THREAD_LOCAL char pidstr[100];
1765 static THREAD_LOCAL struct buffer pid;
1766
1767 if (logsrvs == NULL) {
1768 if (!LIST_ISEMPTY(&global.logsrvs)) {
1769 logsrvs = &global.logsrvs;
1770 }
1771 }
1772 if (!tag || !tag->area)
1773 tag = &global.log_tag;
1774
1775 if (!logsrvs || LIST_ISEMPTY(logsrvs))
1776 return;
1777
1778 if (unlikely(curr_pid != getpid())) {
1779 curr_pid = getpid();
1780 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1781 chunk_initstr(&pid, pidstr);
1782 }
1783
1784 /* Send log messages to syslog server. */
1785 nblogger = 0;
1786 list_for_each_entry(logsrv, logsrvs, list) {
1787 int in_range = 1;
1788
1789 /* we can filter the level of the messages that are sent to each logger */
1790 if (level > logsrv->level)
1791 continue;
1792
1793 if (logsrv->lb.smp_rgs) {
1794 struct smp_log_range *curr_rg;
1795
1796 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1797 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1798 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1799 if (in_range) {
1800 /* Let's consume this range. */
1801 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1802 if (!curr_rg->curr_idx) {
1803 /* If consumed, let's select the next range. */
1804 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1805 }
1806 }
1807 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1808 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1809 }
1810 if (in_range)
1811 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1812 message, size, sd, sd_size, tag->area, tag->data);
1813 }
1814 }
1815
1816
1817 const char sess_cookie[8] = "NIDVEOU7"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */
1818 const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1819 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1820 Set-cookie Updated, unknown, unknown */
1821
1822 /*
1823 * try to write a character if there is enough space, or goto out
1824 */
1825 #define LOGCHAR(x) do { \
1826 if (tmplog < dst + maxsize - 1) { \
1827 *(tmplog++) = (x); \
1828 } else { \
1829 goto out; \
1830 } \
1831 } while(0)
1832
1833
1834 /* Initializes some log data at boot */
init_log()1835 static void init_log()
1836 {
1837 char *tmp;
1838 int i;
1839
1840 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1841 * inside PARAM-VALUE should be escaped with '\' as prefix.
1842 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1843 * details.
1844 */
1845 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1846
1847 tmp = "\"\\]";
1848 while (*tmp) {
1849 ha_bit_set(*tmp, rfc5424_escape_map);
1850 tmp++;
1851 }
1852
1853 /* initialize the log header encoding map : '{|}"#' should be encoded with
1854 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1855 * URL encoding only requires '"', '#' to be encoded as well as non-
1856 * printable characters above.
1857 */
1858 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1859 memset(url_encode_map, 0, sizeof(url_encode_map));
1860 for (i = 0; i < 32; i++) {
1861 ha_bit_set(i, hdr_encode_map);
1862 ha_bit_set(i, url_encode_map);
1863 }
1864 for (i = 127; i < 256; i++) {
1865 ha_bit_set(i, hdr_encode_map);
1866 ha_bit_set(i, url_encode_map);
1867 }
1868
1869 tmp = "\"#{|}";
1870 while (*tmp) {
1871 ha_bit_set(*tmp, hdr_encode_map);
1872 tmp++;
1873 }
1874
1875 tmp = "\"#";
1876 while (*tmp) {
1877 ha_bit_set(*tmp, url_encode_map);
1878 tmp++;
1879 }
1880
1881 /* initialize the http header encoding map. The draft httpbis define the
1882 * header content as:
1883 *
1884 * HTTP-message = start-line
1885 * *( header-field CRLF )
1886 * CRLF
1887 * [ message-body ]
1888 * header-field = field-name ":" OWS field-value OWS
1889 * field-value = *( field-content / obs-fold )
1890 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1891 * obs-fold = CRLF 1*( SP / HTAB )
1892 * field-vchar = VCHAR / obs-text
1893 * VCHAR = %x21-7E
1894 * obs-text = %x80-FF
1895 *
1896 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1897 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
1898 * "obs-fold" is voluntarily forgotten because haproxy remove this.
1899 */
1900 memset(http_encode_map, 0, sizeof(http_encode_map));
1901 for (i = 0x00; i <= 0x08; i++)
1902 ha_bit_set(i, http_encode_map);
1903 for (i = 0x0a; i <= 0x1f; i++)
1904 ha_bit_set(i, http_encode_map);
1905 ha_bit_set(0x7f, http_encode_map);
1906 }
1907
1908 INITCALL0(STG_PREPARE, init_log);
1909
1910 /* Initialize log buffers used for syslog messages */
init_log_buffers()1911 int init_log_buffers()
1912 {
1913 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
1914 logheader_end = NULL;
1915 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
1916 logheader_rfc5424_end = NULL;
1917 logline = my_realloc2(logline, global.max_syslog_len + 1);
1918 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1919 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1920 return 0;
1921 return 1;
1922 }
1923
1924 /* Deinitialize log buffers used for syslog messages */
deinit_log_buffers()1925 void deinit_log_buffers()
1926 {
1927 free(logheader);
1928 free(logheader_rfc5424);
1929 free(logline);
1930 free(logline_rfc5424);
1931 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
1932 logheader = NULL;
1933 logheader_rfc5424 = NULL;
1934 logline = NULL;
1935 logline_rfc5424 = NULL;
1936 }
1937
1938 /* Builds a log line in <dst> based on <list_format>, and stops before reaching
1939 * <maxsize> characters. Returns the size of the output string in characters,
1940 * not counting the trailing zero which is always added if the resulting size
1941 * is not zero. It requires a valid session and optionally a stream. If the
1942 * stream is NULL, default values will be assumed for the stream part.
1943 */
sess_build_logline(struct session * sess,struct stream * s,char * dst,size_t maxsize,struct list * list_format)1944 int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct list *list_format)
1945 {
1946 struct proxy *fe = sess->fe;
1947 struct proxy *be;
1948 struct http_txn *txn;
1949 const struct strm_logs *logs;
1950 struct connection *be_conn;
1951 unsigned int s_flags;
1952 unsigned int uniq_id;
1953 struct buffer chunk;
1954 char *uri;
1955 char *spc;
1956 char *qmark;
1957 char *end;
1958 struct tm tm;
1959 int t_request;
1960 int hdr;
1961 int last_isspace = 1;
1962 int nspaces = 0;
1963 char *tmplog;
1964 char *ret;
1965 int iret;
1966 struct logformat_node *tmp;
1967 struct timeval tv;
1968 struct strm_logs tmp_strm_log;
1969
1970 /* FIXME: let's limit ourselves to frontend logging for now. */
1971
1972 if (likely(s)) {
1973 be = s->be;
1974 txn = s->txn;
1975 be_conn = cs_conn(objt_cs(s->si[1].end));
1976 s_flags = s->flags;
1977 uniq_id = s->uniq_id;
1978 logs = &s->logs;
1979 } else {
1980 /* we have no stream so we first need to initialize a few
1981 * things that are needed later. We do increment the request
1982 * ID so that it's uniquely assigned to this request just as
1983 * if the request had reached the point of being processed.
1984 * A request error is reported as it's the only element we have
1985 * here and which justifies emitting such a log.
1986 */
1987 be = fe;
1988 txn = NULL;
1989 be_conn = NULL;
1990 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
1991 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
1992
1993 /* prepare a valid log structure */
1994 tmp_strm_log.tv_accept = sess->tv_accept;
1995 tmp_strm_log.accept_date = sess->accept_date;
1996 tmp_strm_log.t_handshake = sess->t_handshake;
1997 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1998 tv_zero(&tmp_strm_log.tv_request);
1999 tmp_strm_log.t_queue = -1;
2000 tmp_strm_log.t_connect = -1;
2001 tmp_strm_log.t_data = -1;
2002 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
2003 tmp_strm_log.bytes_in = 0;
2004 tmp_strm_log.bytes_out = 0;
2005 tmp_strm_log.prx_queue_pos = 0;
2006 tmp_strm_log.srv_queue_pos = 0;
2007
2008 logs = &tmp_strm_log;
2009 }
2010
2011 t_request = -1;
2012 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2013 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
2014
2015 tmplog = dst;
2016
2017 /* fill logbuffer */
2018 if (LIST_ISEMPTY(list_format))
2019 return 0;
2020
2021 list_for_each_entry(tmp, list_format, list) {
2022 struct connection *conn;
2023 const char *src = NULL;
2024 struct sample *key;
2025 const struct buffer empty = { };
2026
2027 switch (tmp->type) {
2028 case LOG_FMT_SEPARATOR:
2029 if (!last_isspace) {
2030 LOGCHAR(' ');
2031 last_isspace = 1;
2032 }
2033 break;
2034
2035 case LOG_FMT_TEXT: // text
2036 src = tmp->arg;
2037 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2038 if (iret == 0)
2039 goto out;
2040 tmplog += iret;
2041 last_isspace = 0;
2042 break;
2043
2044 case LOG_FMT_EXPR: // sample expression, may be request or response
2045 key = NULL;
2046 if (tmp->options & LOG_OPT_REQ_CAP && s)
2047 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
2048 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
2049 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
2050 if (tmp->options & LOG_OPT_HTTP)
2051 ret = lf_encode_chunk(tmplog, dst + maxsize,
2052 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
2053 else
2054 ret = lf_text_len(tmplog,
2055 key ? key->data.u.str.area : NULL,
2056 key ? key->data.u.str.data : 0,
2057 dst + maxsize - tmplog,
2058 tmp);
2059 if (ret == 0)
2060 goto out;
2061 tmplog = ret;
2062 last_isspace = 0;
2063 break;
2064
2065 case LOG_FMT_CLIENTIP: // %ci
2066 conn = objt_conn(sess->origin);
2067 if (conn && conn_get_src(conn))
2068 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
2069 else
2070 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2071 if (ret == NULL)
2072 goto out;
2073 tmplog = ret;
2074 last_isspace = 0;
2075 break;
2076
2077 case LOG_FMT_CLIENTPORT: // %cp
2078 conn = objt_conn(sess->origin);
2079 if (conn && conn_get_src(conn)) {
2080 if (conn->src->ss_family == AF_UNIX) {
2081 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
2082 } else {
2083 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
2084 dst + maxsize - tmplog, tmp);
2085 }
2086 }
2087 else
2088 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2089
2090 if (ret == NULL)
2091 goto out;
2092 tmplog = ret;
2093 last_isspace = 0;
2094 break;
2095
2096 case LOG_FMT_FRONTENDIP: // %fi
2097 conn = objt_conn(sess->origin);
2098 if (conn && conn_get_dst(conn)) {
2099 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
2100 }
2101 else
2102 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2103
2104 if (ret == NULL)
2105 goto out;
2106 tmplog = ret;
2107 last_isspace = 0;
2108 break;
2109
2110 case LOG_FMT_FRONTENDPORT: // %fp
2111 conn = objt_conn(sess->origin);
2112 if (conn && conn_get_dst(conn)) {
2113 if (conn->dst->ss_family == AF_UNIX)
2114 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
2115 else
2116 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
2117 }
2118 else
2119 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2120
2121 if (ret == NULL)
2122 goto out;
2123 tmplog = ret;
2124 last_isspace = 0;
2125 break;
2126
2127 case LOG_FMT_BACKENDIP: // %bi
2128 if (be_conn && conn_get_src(be_conn))
2129 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
2130 else
2131 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2132
2133 if (ret == NULL)
2134 goto out;
2135 tmplog = ret;
2136 last_isspace = 0;
2137 break;
2138
2139 case LOG_FMT_BACKENDPORT: // %bp
2140 if (be_conn && conn_get_src(be_conn))
2141 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
2142 else
2143 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2144
2145 if (ret == NULL)
2146 goto out;
2147 tmplog = ret;
2148 last_isspace = 0;
2149 break;
2150
2151 case LOG_FMT_SERVERIP: // %si
2152 if (be_conn && conn_get_dst(be_conn))
2153 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
2154 else
2155 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2156
2157 if (ret == NULL)
2158 goto out;
2159 tmplog = ret;
2160 last_isspace = 0;
2161 break;
2162
2163 case LOG_FMT_SERVERPORT: // %sp
2164 if (be_conn && conn_get_dst(be_conn))
2165 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
2166 else
2167 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2168
2169 if (ret == NULL)
2170 goto out;
2171 tmplog = ret;
2172 last_isspace = 0;
2173 break;
2174
2175 case LOG_FMT_DATE: // %t = accept date
2176 get_localtime(logs->accept_date.tv_sec, &tm);
2177 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
2178 if (ret == NULL)
2179 goto out;
2180 tmplog = ret;
2181 last_isspace = 0;
2182 break;
2183
2184 case LOG_FMT_tr: // %tr = start of request date
2185 /* Note that the timers are valid if we get here */
2186 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
2187 get_localtime(tv.tv_sec, &tm);
2188 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2189 if (ret == NULL)
2190 goto out;
2191 tmplog = ret;
2192 last_isspace = 0;
2193 break;
2194
2195 case LOG_FMT_DATEGMT: // %T = accept date, GMT
2196 get_gmtime(logs->accept_date.tv_sec, &tm);
2197 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2198 if (ret == NULL)
2199 goto out;
2200 tmplog = ret;
2201 last_isspace = 0;
2202 break;
2203
2204 case LOG_FMT_trg: // %trg = start of request date, GMT
2205 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
2206 get_gmtime(tv.tv_sec, &tm);
2207 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2208 if (ret == NULL)
2209 goto out;
2210 tmplog = ret;
2211 last_isspace = 0;
2212 break;
2213
2214 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
2215 get_localtime(logs->accept_date.tv_sec, &tm);
2216 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
2217 if (ret == NULL)
2218 goto out;
2219 tmplog = ret;
2220 last_isspace = 0;
2221 break;
2222
2223 case LOG_FMT_trl: // %trl = start of request date, local
2224 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
2225 get_localtime(tv.tv_sec, &tm);
2226 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2227 if (ret == NULL)
2228 goto out;
2229 tmplog = ret;
2230 last_isspace = 0;
2231 break;
2232
2233 case LOG_FMT_TS: // %Ts
2234 if (tmp->options & LOG_OPT_HEXA) {
2235 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
2236 if (iret < 0 || iret > dst + maxsize - tmplog)
2237 goto out;
2238 last_isspace = 0;
2239 tmplog += iret;
2240 } else {
2241 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
2242 if (ret == NULL)
2243 goto out;
2244 tmplog = ret;
2245 last_isspace = 0;
2246 }
2247 break;
2248
2249 case LOG_FMT_MS: // %ms
2250 if (tmp->options & LOG_OPT_HEXA) {
2251 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
2252 if (iret < 0 || iret > dst + maxsize - tmplog)
2253 goto out;
2254 last_isspace = 0;
2255 tmplog += iret;
2256 } else {
2257 if ((dst + maxsize - tmplog) < 4)
2258 goto out;
2259 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
2260 tmplog, 4);
2261 if (ret == NULL)
2262 goto out;
2263 tmplog = ret;
2264 last_isspace = 0;
2265 }
2266 break;
2267
2268 case LOG_FMT_FRONTEND: // %f
2269 src = fe->id;
2270 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2271 if (ret == NULL)
2272 goto out;
2273 tmplog = ret;
2274 last_isspace = 0;
2275 break;
2276
2277 case LOG_FMT_FRONTEND_XPRT: // %ft
2278 src = fe->id;
2279 if (tmp->options & LOG_OPT_QUOTE)
2280 LOGCHAR('"');
2281 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2282 if (iret == 0)
2283 goto out;
2284 tmplog += iret;
2285 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
2286 LOGCHAR('~');
2287 if (tmp->options & LOG_OPT_QUOTE)
2288 LOGCHAR('"');
2289 last_isspace = 0;
2290 break;
2291 #ifdef USE_OPENSSL
2292 case LOG_FMT_SSL_CIPHER: // %sslc
2293 src = NULL;
2294 conn = objt_conn(sess->origin);
2295 if (conn) {
2296 src = ssl_sock_get_cipher_name(conn);
2297 }
2298 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2299 if (ret == NULL)
2300 goto out;
2301 tmplog = ret;
2302 last_isspace = 0;
2303 break;
2304
2305 case LOG_FMT_SSL_VERSION: // %sslv
2306 src = NULL;
2307 conn = objt_conn(sess->origin);
2308 if (conn) {
2309 src = ssl_sock_get_proto_version(conn);
2310 }
2311 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2312 if (ret == NULL)
2313 goto out;
2314 tmplog = ret;
2315 last_isspace = 0;
2316 break;
2317 #endif
2318 case LOG_FMT_BACKEND: // %b
2319 src = be->id;
2320 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2321 if (ret == NULL)
2322 goto out;
2323 tmplog = ret;
2324 last_isspace = 0;
2325 break;
2326
2327 case LOG_FMT_SERVER: // %s
2328 switch (obj_type(s ? s->target : NULL)) {
2329 case OBJ_TYPE_SERVER:
2330 src = __objt_server(s->target)->id;
2331 break;
2332 case OBJ_TYPE_APPLET:
2333 src = __objt_applet(s->target)->name;
2334 break;
2335 default:
2336 src = "<NOSRV>";
2337 break;
2338 }
2339 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2340 if (ret == NULL)
2341 goto out;
2342 tmplog = ret;
2343 last_isspace = 0;
2344 break;
2345
2346 case LOG_FMT_Th: // %Th = handshake time
2347 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
2348 if (ret == NULL)
2349 goto out;
2350 tmplog = ret;
2351 last_isspace = 0;
2352 break;
2353
2354 case LOG_FMT_Ti: // %Ti = HTTP idle time
2355 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
2356 if (ret == NULL)
2357 goto out;
2358 tmplog = ret;
2359 last_isspace = 0;
2360 break;
2361
2362 case LOG_FMT_TR: // %TR = HTTP request time
2363 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
2364 tmplog, dst + maxsize - tmplog);
2365 if (ret == NULL)
2366 goto out;
2367 tmplog = ret;
2368 last_isspace = 0;
2369 break;
2370
2371 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
2372 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
2373 if (ret == NULL)
2374 goto out;
2375 tmplog = ret;
2376 last_isspace = 0;
2377 break;
2378
2379 case LOG_FMT_TW: // %Tw
2380 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
2381 tmplog, dst + maxsize - tmplog);
2382 if (ret == NULL)
2383 goto out;
2384 tmplog = ret;
2385 last_isspace = 0;
2386 break;
2387
2388 case LOG_FMT_TC: // %Tc
2389 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
2390 tmplog, dst + maxsize - tmplog);
2391 if (ret == NULL)
2392 goto out;
2393 tmplog = ret;
2394 last_isspace = 0;
2395 break;
2396
2397 case LOG_FMT_Tr: // %Tr
2398 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
2399 tmplog, dst + maxsize - tmplog);
2400 if (ret == NULL)
2401 goto out;
2402 tmplog = ret;
2403 last_isspace = 0;
2404 break;
2405
2406 case LOG_FMT_TD: // %Td
2407 if (be->mode == PR_MODE_HTTP)
2408 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
2409 tmplog, dst + maxsize - tmplog);
2410 else
2411 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
2412 tmplog, dst + maxsize - tmplog);
2413 if (ret == NULL)
2414 goto out;
2415 tmplog = ret;
2416 last_isspace = 0;
2417 break;
2418
2419 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2420 if (!(fe->to_log & LW_BYTES))
2421 LOGCHAR('+');
2422 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0),
2423 tmplog, dst + maxsize - tmplog);
2424 if (ret == NULL)
2425 goto out;
2426 tmplog = ret;
2427 last_isspace = 0;
2428 break;
2429
2430 case LOG_FMT_TT: // %Tt = total time
2431 if (!(fe->to_log & LW_BYTES))
2432 LOGCHAR('+');
2433 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
2434 if (ret == NULL)
2435 goto out;
2436 tmplog = ret;
2437 last_isspace = 0;
2438 break;
2439
2440 case LOG_FMT_STATUS: // %ST
2441 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
2442 if (ret == NULL)
2443 goto out;
2444 tmplog = ret;
2445 last_isspace = 0;
2446 break;
2447
2448 case LOG_FMT_BYTES: // %B
2449 if (!(fe->to_log & LW_BYTES))
2450 LOGCHAR('+');
2451 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
2452 if (ret == NULL)
2453 goto out;
2454 tmplog = ret;
2455 last_isspace = 0;
2456 break;
2457
2458 case LOG_FMT_BYTES_UP: // %U
2459 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
2460 if (ret == NULL)
2461 goto out;
2462 tmplog = ret;
2463 last_isspace = 0;
2464 break;
2465
2466 case LOG_FMT_CCLIENT: // %CC
2467 src = txn ? txn->cli_cookie : NULL;
2468 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2469 if (ret == NULL)
2470 goto out;
2471 tmplog = ret;
2472 last_isspace = 0;
2473 break;
2474
2475 case LOG_FMT_CSERVER: // %CS
2476 src = txn ? txn->srv_cookie : NULL;
2477 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2478 if (ret == NULL)
2479 goto out;
2480 tmplog = ret;
2481 last_isspace = 0;
2482 break;
2483
2484 case LOG_FMT_TERMSTATE: // %ts
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 *tmplog = '\0';
2488 last_isspace = 0;
2489 break;
2490
2491 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
2492 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2493 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
2494 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2495 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-');
2496 last_isspace = 0;
2497 break;
2498
2499 case LOG_FMT_ACTCONN: // %ac
2500 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
2501 if (ret == NULL)
2502 goto out;
2503 tmplog = ret;
2504 last_isspace = 0;
2505 break;
2506
2507 case LOG_FMT_FECONN: // %fc
2508 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
2509 if (ret == NULL)
2510 goto out;
2511 tmplog = ret;
2512 last_isspace = 0;
2513 break;
2514
2515 case LOG_FMT_BECONN: // %bc
2516 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
2517 if (ret == NULL)
2518 goto out;
2519 tmplog = ret;
2520 last_isspace = 0;
2521 break;
2522
2523 case LOG_FMT_SRVCONN: // %sc
2524 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
2525 objt_server(s->target)->cur_sess :
2526 0, tmplog, dst + maxsize - tmplog);
2527 if (ret == NULL)
2528 goto out;
2529 tmplog = ret;
2530 last_isspace = 0;
2531 break;
2532
2533 case LOG_FMT_RETRIES: // %rq
2534 if (s_flags & SF_REDISP)
2535 LOGCHAR('+');
2536 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
2537 (be->conn_retries - s->si[1].conn_retries) :
2538 be->conn_retries, tmplog, dst + maxsize - tmplog);
2539 if (ret == NULL)
2540 goto out;
2541 tmplog = ret;
2542 last_isspace = 0;
2543 break;
2544
2545 case LOG_FMT_SRVQUEUE: // %sq
2546 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
2547 if (ret == NULL)
2548 goto out;
2549 tmplog = ret;
2550 last_isspace = 0;
2551 break;
2552
2553 case LOG_FMT_BCKQUEUE: // %bq
2554 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
2555 if (ret == NULL)
2556 goto out;
2557 tmplog = ret;
2558 last_isspace = 0;
2559 break;
2560
2561 case LOG_FMT_HDRREQUEST: // %hr
2562 /* request header */
2563 if (fe->nb_req_cap && s && s->req_cap) {
2564 if (tmp->options & LOG_OPT_QUOTE)
2565 LOGCHAR('"');
2566 LOGCHAR('{');
2567 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2568 if (hdr)
2569 LOGCHAR('|');
2570 if (s->req_cap[hdr] != NULL) {
2571 ret = lf_encode_string(tmplog, dst + maxsize,
2572 '#', hdr_encode_map, s->req_cap[hdr], tmp);
2573 if (ret == NULL || *ret != '\0')
2574 goto out;
2575 tmplog = ret;
2576 }
2577 }
2578 LOGCHAR('}');
2579 if (tmp->options & LOG_OPT_QUOTE)
2580 LOGCHAR('"');
2581 last_isspace = 0;
2582 }
2583 break;
2584
2585 case LOG_FMT_HDRREQUESTLIST: // %hrl
2586 /* request header list */
2587 if (fe->nb_req_cap && s && s->req_cap) {
2588 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2589 if (hdr > 0)
2590 LOGCHAR(' ');
2591 if (tmp->options & LOG_OPT_QUOTE)
2592 LOGCHAR('"');
2593 if (s->req_cap[hdr] != NULL) {
2594 ret = lf_encode_string(tmplog, dst + maxsize,
2595 '#', hdr_encode_map, s->req_cap[hdr], tmp);
2596 if (ret == NULL || *ret != '\0')
2597 goto out;
2598 tmplog = ret;
2599 } else if (!(tmp->options & LOG_OPT_QUOTE))
2600 LOGCHAR('-');
2601 if (tmp->options & LOG_OPT_QUOTE)
2602 LOGCHAR('"');
2603 last_isspace = 0;
2604 }
2605 }
2606 break;
2607
2608
2609 case LOG_FMT_HDRRESPONS: // %hs
2610 /* response header */
2611 if (fe->nb_rsp_cap && s && s->res_cap) {
2612 if (tmp->options & LOG_OPT_QUOTE)
2613 LOGCHAR('"');
2614 LOGCHAR('{');
2615 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2616 if (hdr)
2617 LOGCHAR('|');
2618 if (s->res_cap[hdr] != NULL) {
2619 ret = lf_encode_string(tmplog, dst + maxsize,
2620 '#', hdr_encode_map, s->res_cap[hdr], tmp);
2621 if (ret == NULL || *ret != '\0')
2622 goto out;
2623 tmplog = ret;
2624 }
2625 }
2626 LOGCHAR('}');
2627 last_isspace = 0;
2628 if (tmp->options & LOG_OPT_QUOTE)
2629 LOGCHAR('"');
2630 }
2631 break;
2632
2633 case LOG_FMT_HDRRESPONSLIST: // %hsl
2634 /* response header list */
2635 if (fe->nb_rsp_cap && s && s->res_cap) {
2636 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2637 if (hdr > 0)
2638 LOGCHAR(' ');
2639 if (tmp->options & LOG_OPT_QUOTE)
2640 LOGCHAR('"');
2641 if (s->res_cap[hdr] != NULL) {
2642 ret = lf_encode_string(tmplog, dst + maxsize,
2643 '#', hdr_encode_map, s->res_cap[hdr], tmp);
2644 if (ret == NULL || *ret != '\0')
2645 goto out;
2646 tmplog = ret;
2647 } else if (!(tmp->options & LOG_OPT_QUOTE))
2648 LOGCHAR('-');
2649 if (tmp->options & LOG_OPT_QUOTE)
2650 LOGCHAR('"');
2651 last_isspace = 0;
2652 }
2653 }
2654 break;
2655
2656 case LOG_FMT_REQ: // %r
2657 /* Request */
2658 if (tmp->options & LOG_OPT_QUOTE)
2659 LOGCHAR('"');
2660 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2661 ret = lf_encode_string(tmplog, dst + maxsize,
2662 '#', url_encode_map, uri, tmp);
2663 if (ret == NULL || *ret != '\0')
2664 goto out;
2665 tmplog = ret;
2666 if (tmp->options & LOG_OPT_QUOTE)
2667 LOGCHAR('"');
2668 last_isspace = 0;
2669 break;
2670
2671 case LOG_FMT_HTTP_PATH: // %HP
2672 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2673
2674 if (tmp->options & LOG_OPT_QUOTE)
2675 LOGCHAR('"');
2676
2677 end = uri + strlen(uri);
2678 // look for the first whitespace character
2679 while (uri < end && !HTTP_IS_SPHT(*uri))
2680 uri++;
2681
2682 // keep advancing past multiple spaces
2683 while (uri < end && HTTP_IS_SPHT(*uri)) {
2684 uri++; nspaces++;
2685 }
2686
2687 // look for first space or question mark after url
2688 spc = uri;
2689 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2690 spc++;
2691
2692 if (!txn || !txn->uri || nspaces == 0) {
2693 chunk.area = "<BADREQ>";
2694 chunk.data = strlen("<BADREQ>");
2695 } else {
2696 chunk.area = uri;
2697 chunk.data = spc - uri;
2698 }
2699
2700 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2701 if (ret == NULL || *ret != '\0')
2702 goto out;
2703
2704 tmplog = ret;
2705 if (tmp->options & LOG_OPT_QUOTE)
2706 LOGCHAR('"');
2707
2708 last_isspace = 0;
2709 break;
2710
2711 case LOG_FMT_HTTP_QUERY: // %HQ
2712 if (tmp->options & LOG_OPT_QUOTE)
2713 LOGCHAR('"');
2714
2715 if (!txn || !txn->uri) {
2716 chunk.area = "<BADREQ>";
2717 chunk.data = strlen("<BADREQ>");
2718 } else {
2719 uri = txn->uri;
2720 end = uri + strlen(uri);
2721 // look for the first question mark
2722 while (uri < end && *uri != '?')
2723 uri++;
2724
2725 qmark = uri;
2726 // look for first space or question mark after url
2727 while (uri < end && !HTTP_IS_SPHT(*uri))
2728 uri++;
2729
2730 chunk.area = qmark;
2731 chunk.data = uri - qmark;
2732 }
2733
2734 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2735 if (ret == NULL || *ret != '\0')
2736 goto out;
2737
2738 tmplog = ret;
2739 if (tmp->options & LOG_OPT_QUOTE)
2740 LOGCHAR('"');
2741
2742 last_isspace = 0;
2743 break;
2744
2745 case LOG_FMT_HTTP_URI: // %HU
2746 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2747
2748 if (tmp->options & LOG_OPT_QUOTE)
2749 LOGCHAR('"');
2750
2751 end = uri + strlen(uri);
2752 // look for the first whitespace character
2753 while (uri < end && !HTTP_IS_SPHT(*uri))
2754 uri++;
2755
2756 // keep advancing past multiple spaces
2757 while (uri < end && HTTP_IS_SPHT(*uri)) {
2758 uri++; nspaces++;
2759 }
2760
2761 // look for first space after url
2762 spc = uri;
2763 while (spc < end && !HTTP_IS_SPHT(*spc))
2764 spc++;
2765
2766 if (!txn || !txn->uri || nspaces == 0) {
2767 chunk.area = "<BADREQ>";
2768 chunk.data = strlen("<BADREQ>");
2769 } else {
2770 chunk.area = uri;
2771 chunk.data = spc - uri;
2772 }
2773
2774 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2775 if (ret == NULL || *ret != '\0')
2776 goto out;
2777
2778 tmplog = ret;
2779 if (tmp->options & LOG_OPT_QUOTE)
2780 LOGCHAR('"');
2781
2782 last_isspace = 0;
2783 break;
2784
2785 case LOG_FMT_HTTP_METHOD: // %HM
2786 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2787 if (tmp->options & LOG_OPT_QUOTE)
2788 LOGCHAR('"');
2789
2790 end = uri + strlen(uri);
2791 // look for the first whitespace character
2792 spc = uri;
2793 while (spc < end && !HTTP_IS_SPHT(*spc))
2794 spc++;
2795
2796 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
2797 chunk.area = "<BADREQ>";
2798 chunk.data = strlen("<BADREQ>");
2799 } else {
2800 chunk.area = uri;
2801 chunk.data = spc - uri;
2802 }
2803
2804 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2805 if (ret == NULL || *ret != '\0')
2806 goto out;
2807
2808 tmplog = ret;
2809 if (tmp->options & LOG_OPT_QUOTE)
2810 LOGCHAR('"');
2811
2812 last_isspace = 0;
2813 break;
2814
2815 case LOG_FMT_HTTP_VERSION: // %HV
2816 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2817 if (tmp->options & LOG_OPT_QUOTE)
2818 LOGCHAR('"');
2819
2820 end = uri + strlen(uri);
2821 // look for the first whitespace character
2822 while (uri < end && !HTTP_IS_SPHT(*uri))
2823 uri++;
2824
2825 // keep advancing past multiple spaces
2826 while (uri < end && HTTP_IS_SPHT(*uri)) {
2827 uri++; nspaces++;
2828 }
2829
2830 // look for the next whitespace character
2831 while (uri < end && !HTTP_IS_SPHT(*uri))
2832 uri++;
2833
2834 // keep advancing past multiple spaces
2835 while (uri < end && HTTP_IS_SPHT(*uri))
2836 uri++;
2837
2838 if (!txn || !txn->uri || nspaces == 0) {
2839 chunk.area = "<BADREQ>";
2840 chunk.data = strlen("<BADREQ>");
2841 } else if (uri == end) {
2842 chunk.area = "HTTP/0.9";
2843 chunk.data = strlen("HTTP/0.9");
2844 } else {
2845 chunk.area = uri;
2846 chunk.data = end - uri;
2847 }
2848
2849 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2850 if (ret == NULL || *ret != '\0')
2851 goto out;
2852
2853 tmplog = ret;
2854 if (tmp->options & LOG_OPT_QUOTE)
2855 LOGCHAR('"');
2856
2857 last_isspace = 0;
2858 break;
2859
2860 case LOG_FMT_COUNTER: // %rt
2861 if (tmp->options & LOG_OPT_HEXA) {
2862 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
2863 if (iret < 0 || iret > dst + maxsize - tmplog)
2864 goto out;
2865 last_isspace = 0;
2866 tmplog += iret;
2867 } else {
2868 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
2869 if (ret == NULL)
2870 goto out;
2871 tmplog = ret;
2872 last_isspace = 0;
2873 }
2874 break;
2875
2876 case LOG_FMT_LOGCNT: // %lc
2877 if (tmp->options & LOG_OPT_HEXA) {
2878 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
2879 if (iret < 0 || iret > dst + maxsize - tmplog)
2880 goto out;
2881 last_isspace = 0;
2882 tmplog += iret;
2883 } else {
2884 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
2885 if (ret == NULL)
2886 goto out;
2887 tmplog = ret;
2888 last_isspace = 0;
2889 }
2890 break;
2891
2892 case LOG_FMT_HOSTNAME: // %H
2893 src = hostname;
2894 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2895 if (ret == NULL)
2896 goto out;
2897 tmplog = ret;
2898 last_isspace = 0;
2899 break;
2900
2901 case LOG_FMT_PID: // %pid
2902 if (tmp->options & LOG_OPT_HEXA) {
2903 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2904 if (iret < 0 || iret > dst + maxsize - tmplog)
2905 goto out;
2906 last_isspace = 0;
2907 tmplog += iret;
2908 } else {
2909 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2910 if (ret == NULL)
2911 goto out;
2912 tmplog = ret;
2913 last_isspace = 0;
2914 }
2915 break;
2916
2917 case LOG_FMT_UNIQUEID: // %ID
2918 ret = NULL;
2919 src = s ? s->unique_id : NULL;
2920 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
2921 if (ret == NULL)
2922 goto out;
2923 tmplog = ret;
2924 last_isspace = 0;
2925 break;
2926
2927 }
2928 }
2929
2930 out:
2931 /* *tmplog is a unused character */
2932 *tmplog = '\0';
2933 return tmplog - dst;
2934
2935 }
2936
2937 /*
2938 * send a log for the stream when we have enough info about it.
2939 * Will not log if the frontend has no log defined.
2940 */
strm_log(struct stream * s)2941 void strm_log(struct stream *s)
2942 {
2943 struct session *sess = s->sess;
2944 int size, err, level;
2945 int sd_size = 0;
2946
2947 /* if we don't want to log normal traffic, return now */
2948 err = (s->flags & SF_REDISP) ||
2949 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2950 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
2951 (s->si[1].conn_retries != s->be->conn_retries)) ||
2952 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
2953
2954 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
2955 return;
2956
2957 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2958 return;
2959
2960 if (s->logs.level) { /* loglevel was overridden */
2961 if (s->logs.level == -1) {
2962 s->logs.logwait = 0; /* logs disabled */
2963 return;
2964 }
2965 level = s->logs.level - 1;
2966 }
2967 else {
2968 level = LOG_INFO;
2969 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
2970 level = LOG_ERR;
2971 }
2972
2973 /* if unique-id was not generated */
2974 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
2975 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
2976 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
2977 }
2978
2979 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2980 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2981 &sess->fe->logformat_sd);
2982 }
2983
2984 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
2985 if (size > 0) {
2986 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
2987 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
2988 logline, size + 1, logline_rfc5424, sd_size);
2989 s->logs.logwait = 0;
2990 }
2991 }
2992
2993 /*
2994 * send a minimalist log for the session. Will not log if the frontend has no
2995 * log defined. It is assumed that this is only used to report anomalies that
2996 * cannot lead to the creation of a regular stream. Because of this the log
2997 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2998 * in the frontend. The caller must simply know that it should not call this
2999 * function to report unimportant events. It is safe to call this function with
3000 * sess==NULL (will not do anything).
3001 */
sess_log(struct session * sess)3002 void sess_log(struct session *sess)
3003 {
3004 int size, level;
3005 int sd_size = 0;
3006
3007 if (!sess)
3008 return;
3009
3010 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3011 return;
3012
3013 level = LOG_INFO;
3014 if (sess->fe->options2 & PR_O2_LOGERRORS)
3015 level = LOG_ERR;
3016
3017 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3018 sd_size = sess_build_logline(sess, NULL,
3019 logline_rfc5424, global.max_syslog_len,
3020 &sess->fe->logformat_sd);
3021 }
3022
3023 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
3024 if (size > 0) {
3025 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
3026 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3027 logline, size + 1, logline_rfc5424, sd_size);
3028 }
3029 }
3030
app_log(struct list * logsrvs,struct buffer * tag,int level,const char * format,...)3031 void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3032 {
3033 va_list argp;
3034 int data_len;
3035
3036 if (level < 0 || format == NULL || logline == NULL)
3037 return;
3038
3039 va_start(argp, format);
3040 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3041 if (data_len < 0 || data_len > global.max_syslog_len)
3042 data_len = global.max_syslog_len;
3043 va_end(argp);
3044
3045 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3046 }
3047
3048 /* 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)3049 static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
3050 {
3051 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
3052 return 1;
3053
3054 if (!startup_logs)
3055 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
3056
3057 return ring_attach_cli(startup_logs, appctx);
3058 }
3059
3060 /* register cli keywords */
3061 static struct cli_kw_list cli_kws = {{ },{
3062 { { "show", "startup-logs", NULL },
3063 "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
3064 {{},}
3065 }};
3066
3067 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3068
3069 REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3070 REGISTER_PER_THREAD_FREE(deinit_log_buffers);
3071
3072 /*
3073 * Local variables:
3074 * c-indent-level: 8
3075 * c-basic-offset: 8
3076 * End:
3077 */
3078