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