blob: b0ae9c685e240e3b0e6320df34dc29986d813439 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * General logging functions.
3 *
Willy Tarreaub7f694f2008-06-22 17:18:02 +02004 * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
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
Willy Tarreau8a3f52f2012-12-20 21:23:42 +010013#include <ctype.h>
Willy Tarreauc8f24f82007-11-30 18:38:35 +010014#include <fcntl.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020015#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>
Robert Tsai81ae1952007-12-05 10:47:29 +010022#include <errno.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023
24#include <sys/time.h>
Willy Tarreau077edcb2016-08-10 18:30:56 +020025#include <sys/uio.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020027#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020028#include <haproxy/applet-t.h>
Emeric Brun12941c82020-07-07 14:19:42 +020029#include <haproxy/cfgparse.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020030#include <haproxy/fd.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020031#include <haproxy/frontend.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020032#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020033#include <haproxy/http.h>
Emeric Brun12941c82020-07-07 14:19:42 +020034#include <haproxy/listener.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020035#include <haproxy/log.h>
Emeric Brun12941c82020-07-07 14:19:42 +020036#include <haproxy/proxy.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020037#include <haproxy/sample.h>
38#include <haproxy/sink.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020039#include <haproxy/ssl_sock.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020040#include <haproxy/stream.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020041#include <haproxy/stream_interface.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020042#include <haproxy/time.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020043#include <haproxy/tools.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
Emeric Brun45c457a2020-07-09 23:23:34 +020045/* global recv logs counter */
46int cum_log_messages;
Willy Tarreaubaaee002006-06-26 02:48:02 +020047
Emeric Brun12941c82020-07-07 14:19:42 +020048/* log forward proxy list */
49struct proxy *cfg_log_forward;
50
Emeric Brun54648852020-07-06 15:54:06 +020051struct log_fmt_st {
Dragan Dosen43885c72015-10-01 13:18:13 +020052 char *name;
Dragan Dosen43885c72015-10-01 13:18:13 +020053};
54
Emeric Brun54648852020-07-06 15:54:06 +020055static const struct log_fmt_st log_formats[LOG_FORMATS] = {
Emeric Brun0237c4e2020-11-27 16:24:34 +010056 [LOG_FORMAT_LOCAL] = {
57 .name = "local",
58 },
Dragan Dosen43885c72015-10-01 13:18:13 +020059 [LOG_FORMAT_RFC3164] = {
60 .name = "rfc3164",
Dragan Dosen43885c72015-10-01 13:18:13 +020061 },
62 [LOG_FORMAT_RFC5424] = {
63 .name = "rfc5424",
Emeric Brun54648852020-07-06 15:54:06 +020064 },
65 [LOG_FORMAT_PRIO] = {
66 .name = "priority",
Willy Tarreaue8746a02018-11-12 08:45:00 +010067 },
68 [LOG_FORMAT_SHORT] = {
69 .name = "short",
Emeric Brun54648852020-07-06 15:54:06 +020070 },
71 [LOG_FORMAT_TIMED] = {
72 .name = "timed",
73 },
74 [LOG_FORMAT_ISO] = {
75 .name = "iso",
Willy Tarreaue8746a02018-11-12 08:45:00 +010076 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010077 [LOG_FORMAT_RAW] = {
78 .name = "raw",
Willy Tarreauc1b06452018-11-12 11:57:56 +010079 },
Dragan Dosen1322d092015-09-22 16:05:32 +020080};
81
Dragan Dosen835b9212016-02-12 13:23:03 +010082/*
83 * This map is used with all the FD_* macros to check whether a particular bit
Willy Tarreau1bfd6022019-06-07 11:10:07 +020084 * is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
85 * bytes which should be escaped. When ha_bit_test() returns non-zero, it means
86 * that the byte should be escaped. Be careful to always pass bytes from 0 to
87 * 255 exclusively to the macros.
Dragan Dosen835b9212016-02-12 13:23:03 +010088 */
Willy Tarreau1bfd6022019-06-07 11:10:07 +020089long rfc5424_escape_map[(256/8) / sizeof(long)];
90long hdr_encode_map[(256/8) / sizeof(long)];
91long url_encode_map[(256/8) / sizeof(long)];
92long http_encode_map[(256/8) / sizeof(long)];
Dragan Dosen835b9212016-02-12 13:23:03 +010093
Dragan Dosen835b9212016-02-12 13:23:03 +010094
Willy Tarreaubaaee002006-06-26 02:48:02 +020095const char *log_facilities[NB_LOG_FACILITIES] = {
96 "kern", "user", "mail", "daemon",
97 "auth", "syslog", "lpr", "news",
98 "uucp", "cron", "auth2", "ftp",
99 "ntp", "audit", "alert", "cron2",
100 "local0", "local1", "local2", "local3",
101 "local4", "local5", "local6", "local7"
102};
103
Willy Tarreaubaaee002006-06-26 02:48:02 +0200104const char *log_levels[NB_LOG_LEVELS] = {
105 "emerg", "alert", "crit", "err",
106 "warning", "notice", "info", "debug"
107};
108
Willy Tarreau570f2212013-06-10 16:42:09 +0200109const char sess_term_cond[16] = "-LcCsSPRIDKUIIII"; /* normal, Local, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal, Down, Killed, Up, -- */
Willy Tarreaub8750a82006-09-03 09:56:00 +0200110const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200111
William Lallemand723b73a2012-02-08 16:37:49 +0100112
113/* log_format */
114struct logformat_type {
115 char *name;
116 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100117 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200118 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100119 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
William Lallemand723b73a2012-02-08 16:37:49 +0100120};
121
William Lallemandb7ff6a32012-03-02 14:35:21 +0100122int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
123
William Lallemand723b73a2012-02-08 16:37:49 +0100124/* log_format variable names */
125static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200126 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100127
128 /* please keep these lines sorted ! */
129 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
130 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
131 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
132 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
Tim Duesterhuscf6e0c82020-03-13 12:34:24 +0100133 { "ID", LOG_FMT_UNIQUEID, PR_MODE_TCP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200134 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200135 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200136 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100137 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200138 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
139 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
140 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
141 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
142 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
143 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200144 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100145 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200146 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Damien Claisse57c8eb92020-04-28 12:09:19 +0000147 { "Tu", LOG_FMT_TU, PR_MODE_TCP, LW_BYTES, NULL }, /* Tu = Tt -Ti */
Willy Tarreau2beef582012-12-20 17:22:52 +0100148 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
149 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200150 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100151 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200152 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100153 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
154 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200155 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200156 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
157 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100158 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
159 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200160 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
161 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100162 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200163 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
164 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
165 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
166 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000167 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
Maciej Zdebfcdfd852020-11-30 18:27:47 +0000168 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP relative or absolute path */
169 { "HPO", LOG_FMT_HTTP_PATH_ONLY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path only (without host nor query string) */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000170 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000171 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
172 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200173 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100174 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200175 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100176 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
177 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100178 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100179 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
180 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
181 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
182 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
183 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200184 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
185 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100186 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200187 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
188 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
189 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100190 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
191 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
William Lallemand5e19a282012-04-02 16:22:10 +0200192 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100193};
194
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200195char 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
196char 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";
Willy Tarreau2beef582012-12-20 17:22:52 +0100197char default_tcp_log_format[] = "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq";
William Lallemand723b73a2012-02-08 16:37:49 +0100198char *log_format = NULL;
199
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200200/* Default string used for structured-data part in RFC5424 formatted
201 * syslog messages.
202 */
203char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200204
Willy Tarreau13ef7732018-11-12 07:25:28 +0100205/* total number of dropped logs */
206unsigned int dropped_logs = 0;
207
Dragan Dosen59cee972015-09-19 22:09:02 +0200208/* This is a global syslog message buffer, common to all outgoing
209 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100210 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200211THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100212
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200213/* A global syslog message buffer, common to all RFC5424 syslog messages.
214 * Currently, it is used for generating the structured-data part.
215 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200216THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200217
William Lallemand723b73a2012-02-08 16:37:49 +0100218struct logformat_var_args {
219 char *name;
220 int mask;
221};
222
223struct logformat_var_args var_args_list[] = {
224// global
225 { "M", LOG_OPT_MANDATORY },
226 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200227 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100228 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100229 { 0, 0 }
230};
231
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200232/* return the name of the directive used in the current proxy for which we're
233 * currently parsing a header, when it is known.
234 */
235static inline const char *fmt_directive(const struct proxy *curproxy)
236{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100237 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200238 case ARGC_ACL:
239 return "acl";
240 case ARGC_STK:
241 return "stick";
242 case ARGC_TRK:
243 return "track-sc";
244 case ARGC_LOG:
245 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200246 case ARGC_LOGSD:
247 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100248 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100249 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100250 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100251 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200252 case ARGC_UIF:
253 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100254 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200255 return "redirect";
256 case ARGC_CAP:
257 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200258 case ARGC_SRV:
259 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200260 case ARGC_SPOE:
261 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100262 case ARGC_UBK:
263 return "use_backend";
Christopher Faulet3b967c12020-05-15 15:47:44 +0200264 case ARGC_HERR:
265 return "http-error";
Miroslav Zagorac7f8314c2020-12-09 16:31:48 +0100266 case ARGC_OT:
267 return "ot-scope";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100268 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200269 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100270 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200271}
272
William Lallemand723b73a2012-02-08 16:37:49 +0100273/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100274 * callback used to configure addr source retrieval
275 */
276int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
277{
278 curproxy->options2 |= PR_O2_SRC_ADDR;
279
280 return 0;
281}
282
283
284/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100285 * Parse args in a logformat_var. Returns 0 in error
286 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100287 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100288int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100289{
290 int i = 0;
291 int end = 0;
292 int flags = 0; // 1 = + 2 = -
293 char *sp = NULL; // start pointer
294
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100295 if (args == NULL) {
296 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100297 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100298 }
William Lallemand723b73a2012-02-08 16:37:49 +0100299
300 while (1) {
301 if (*args == '\0')
302 end = 1;
303
304 if (*args == '+') {
305 // add flag
306 sp = args + 1;
307 flags = 1;
308 }
309 if (*args == '-') {
310 // delete flag
311 sp = args + 1;
312 flags = 2;
313 }
314
315 if (*args == '\0' || *args == ',') {
316 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100317 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100318 if (strcmp(sp, var_args_list[i].name) == 0) {
319 if (flags == 1) {
320 node->options |= var_args_list[i].mask;
321 break;
322 } else if (flags == 2) {
323 node->options &= ~var_args_list[i].mask;
324 break;
325 }
326 }
327 }
328 sp = NULL;
329 if (end)
330 break;
331 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100332 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100333 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100334 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100335}
336
337/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100338 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
339 * must pass the args part in the <arg> pointer with its length in <arg_len>,
340 * and varname with its length in <var> and <var_len> respectively. <arg> is
341 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100342 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100343 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100344int parse_logformat_var(char *arg, int arg_len, char *var, int var_len, struct proxy *curproxy, struct list *list_format, int *defoptions, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100345{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100346 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200347 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100348
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100349 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
350 if (strlen(logformat_keywords[j].name) == var_len &&
351 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
352 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200353 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100354 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100355 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200356 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100357 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100358 node->type = logformat_keywords[j].type;
359 node->options = *defoptions;
360 if (arg_len) {
361 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100362 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200363 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100364 }
365 if (node->type == LOG_FMT_GLOBAL) {
366 *defoptions = node->options;
367 free(node->arg);
368 free(node);
369 } else {
370 if (logformat_keywords[j].config_callback &&
371 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200372 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100373 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100374 curproxy->to_log |= logformat_keywords[j].lw;
Willy Tarreau2b718102021-04-21 07:32:39 +0200375 LIST_APPEND(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100376 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100377 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100378 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100379 memprintf(err, "format variable '%s' is reserved for HTTP mode",
380 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200381 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100382 }
William Lallemand723b73a2012-02-08 16:37:49 +0100383 }
384 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100385
386 j = var[var_len];
387 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100388 memprintf(err, "no such format variable '%s'. If you wanted to emit the '%%' character verbatim, you need to use '%%%%'", var);
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100389 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200390
391 error_free:
392 if (node) {
393 free(node->arg);
394 free(node);
395 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100396 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100397}
398
399/*
400 * push to the logformat linked list
401 *
402 * start: start pointer
403 * end: end text pointer
404 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100405 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100406 *
407 * LOG_TEXT: copy chars from start to end excluding end.
408 *
409*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100410int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100411{
412 char *str;
413
Willy Tarreaua3571662012-12-20 21:59:12 +0100414 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200415 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100416 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100417 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100418 return 0;
419 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200420 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100421 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100422 str[end - start] = '\0';
423 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100424 node->type = LOG_FMT_TEXT; // type string
Willy Tarreau2b718102021-04-21 07:32:39 +0200425 LIST_APPEND(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100426 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200427 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100428 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100429 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100430 return 0;
431 }
William Lallemand1d705562012-03-12 12:46:41 +0100432 node->type = LOG_FMT_SEPARATOR;
Willy Tarreau2b718102021-04-21 07:32:39 +0200433 LIST_APPEND(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100434 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100435 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100436}
437
438/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100439 * Parse the sample fetch expression <text> and add a node to <list_format> upon
440 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100441 * should work. The curpx->conf.args.ctx must be set by the caller. If an end pointer
442 * is passed in <endptr>, it will be updated with the pointer to the first character
443 * not part of the sample expression.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100444 *
445 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100446 */
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100447int 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, char **endptr)
Willy Tarreauc8368452012-12-21 00:09:23 +0100448{
449 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200450 struct sample_expr *expr = NULL;
451 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100452 int cmd_arg;
453
454 cmd[0] = text;
455 cmd[1] = "";
456 cmd_arg = 0;
457
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100458 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args, endptr);
Willy Tarreauc8368452012-12-21 00:09:23 +0100459 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100460 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200461 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100462 }
463
Vincent Bernat02779b62016-04-03 13:48:43 +0200464 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100465 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100466 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200467 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100468 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100469 node->type = LOG_FMT_EXPR;
470 node->expr = expr;
471 node->options = options;
472
473 if (arg_len) {
474 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100475 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200476 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100477 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100478 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100479 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
480
Willy Tarreau434c57c2013-01-08 01:10:24 +0100481 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100482 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
483
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100484 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100485 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
486 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200487 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100488 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100489
Christopher Faulet581db2b2021-03-26 10:02:46 +0100490 if ((options & LOG_OPT_HTTP) && (expr->fetch->use & (SMP_USE_L6REQ|SMP_USE_L6RES))) {
491 ha_warning("parsing [%s:%d] : L6 sample fetch <%s> ignored in HTTP log-format string.\n",
492 curpx->conf.args.file, curpx->conf.args.line, text);
493 }
494
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200495 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreauc8368452012-12-21 00:09:23 +0100496 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100497 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100498
William Lallemand65ad6e12014-01-31 15:08:02 +0100499 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
500 * needed with some sample fetches (eg: ssl*). We always set it for
501 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100502 */
503 curpx->to_log |= LW_XPRT;
Christopher Fauletd2236cd2020-04-06 18:29:14 +0200504 if (curpx->http_needed)
505 curpx->to_log |= LW_REQ;
Willy Tarreau2b718102021-04-21 07:32:39 +0200506 LIST_APPEND(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100507 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200508
509 error_free:
510 release_sample_expr(expr);
511 if (node) {
512 free(node->arg);
513 free(node);
514 }
515 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100516}
517
518/*
William Lallemand723b73a2012-02-08 16:37:49 +0100519 * Parse the log_format string and fill a linked list.
520 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200521 * You can set arguments using { } : %{many arguments}varname.
522 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100523 *
Ilya Shipitsinae40dbc2020-04-04 12:59:53 +0500524 * fmt: the string to parse
William Lallemand1d705562012-03-12 12:46:41 +0100525 * curproxy: the proxy affected
526 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100527 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100528 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100529 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100530 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100531 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100532int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list *list_format, int options, int cap, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100533{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100534 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100535 char *arg = NULL; /* start pointer for args */
536 char *var = NULL; /* start pointer for vars */
537 int arg_len = 0;
538 int var_len = 0;
539 int cformat; /* current token format */
540 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100541 struct logformat_node *tmplf, *back;
542
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100543 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100544 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100545 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100546 return 0;
547 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200548 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200549
William Lallemand723b73a2012-02-08 16:37:49 +0100550 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100551 list_for_each_entry_safe(tmplf, back, list_format, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200552 LIST_DELETE(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200553 release_sample_expr(tmplf->expr);
554 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100555 free(tmplf);
556 }
557
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100558 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100559 pformat = cformat;
560
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100561 if (!*str)
562 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100563
Joseph Herlant85b40592018-11-15 12:10:04 -0800564 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100565 * second have all common paths processed at one place. The common paths are the ones
566 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
567 * We use the common LF_INIT state to dispatch to the different final states.
568 */
569 switch (pformat) {
570 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100571 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100572 arg_len = var_len = 0;
573 if (*str == '{') { // optional argument
574 cformat = LF_STARG;
575 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100576 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100577 else if (*str == '[') {
578 cformat = LF_STEXPR;
579 var = str + 1; // store expr in variable name
580 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100581 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100582 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100583 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100584 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100585 else if (*str == '%')
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500586 cformat = LF_TEXT; // convert this character to a literal (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100587 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100588 /* single '%' followed by blank or digit, send them both */
589 cformat = LF_TEXT;
590 pformat = LF_TEXT; /* finally we include the previous char as well */
591 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600592 memprintf(err, "unexpected variable name near '%c' at position %d line : '%s'. Maybe you want to write a single '%%', use the syntax '%%%%'",
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100593 *str, (int)(str - backfmt), fmt);
Willy Tarreau51013e82019-12-11 12:05:39 +0100594 goto fail;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100595
596 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100597 else
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500598 cformat = LF_INIT; // handle other cases of literals
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100599 break;
600
601 case LF_STARG: // text immediately following '%{'
602 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100603 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100604 arg_len = str - arg;
605 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100606 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100607 break;
608
609 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100610 if (*str == '[') {
611 cformat = LF_STEXPR;
612 var = str + 1; // store expr in variable name
613 break;
614 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100615 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100616 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100617 var = str;
618 break;
619 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100620 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Willy Tarreau51013e82019-12-11 12:05:39 +0100621 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100622
Willy Tarreauc8368452012-12-21 00:09:23 +0100623 case LF_STEXPR: // text immediately following '%['
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100624 /* the whole sample expression is parsed at once,
625 * returning the pointer to the first character not
626 * part of the expression, which MUST be the trailing
627 * angle bracket.
628 */
629 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &str))
630 goto fail;
631
632 if (*str == ']') {
633 // end of arg, go on with next state
634 cformat = pformat = LF_EDEXPR;
635 sp = str;
636 }
637 else {
638 char c = *str;
639 *str = 0;
Willy Tarreau90807112020-02-25 08:16:33 +0100640 if (isprint((unsigned char)c))
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100641 memprintf(err, "expected ']' after '%s', but found '%c'", var, c);
642 else
643 memprintf(err, "missing ']' after '%s'", var);
Dragan Dosen2866acf2020-06-30 21:16:43 +0200644 goto fail;
Willy Tarreauc8368452012-12-21 00:09:23 +0100645 }
646 break;
647
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100648 case LF_VAR: // text part of a variable name
649 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100650 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100651 cformat = LF_INIT; // not variable name anymore
652 break;
653
Willy Tarreauc8368452012-12-21 00:09:23 +0100654 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100655 cformat = LF_INIT;
656 }
657
658 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
659 switch (*str) {
660 case '%': cformat = LF_STARTVAR; break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100661 case 0 : cformat = LF_END; break;
Dragan Dosen1e3b16f2020-06-23 18:16:44 +0200662 case ' ':
663 if (options & LOG_OPT_MERGE_SPACES) {
664 cformat = LF_SEPARATOR;
665 break;
666 }
667 /* fall through */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100668 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100669 }
670 }
671
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100672 if (cformat != pformat || pformat == LF_SEPARATOR) {
673 switch (pformat) {
674 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100675 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100676 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100677 break;
678 case LF_TEXT:
679 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100680 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100681 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100682 break;
683 }
684 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100685 }
686 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100687
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100688 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100689 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Willy Tarreau51013e82019-12-11 12:05:39 +0100690 goto fail;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100691 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100692 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100693
694 return 1;
Willy Tarreau51013e82019-12-11 12:05:39 +0100695 fail:
696 free(backfmt);
697 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100698}
699
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200700/*
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500701 * Parse the first range of indexes from a string made of a list of comma separated
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200702 * ranges of indexes. Note that an index may be considered as a particular range
703 * with a high limit to the low limit.
704 */
705int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
706{
707 char *end, *p;
708
709 *low = *high = 0;
710
711 p = *arg;
712 end = strchr(p, ',');
713 if (!end)
714 end = p + strlen(p);
715
716 *high = *low = read_uint((const char **)&p, end);
717 if (!*low || (p != end && *p != '-'))
718 goto err;
719
720 if (p == end)
721 goto done;
722
723 p++;
724 *high = read_uint((const char **)&p, end);
725 if (!*high || *high <= *low || p != end)
726 goto err;
727
728 done:
729 if (*end == ',')
730 end++;
731 *arg = end;
732 return 1;
733
734 err:
735 memprintf(err, "wrong sample range '%s'", *arg);
736 return 0;
737}
738
739/*
740 * Returns 1 if the range defined by <low> and <high> overlaps
741 * one of them in <rgs> array of ranges with <sz> the size of this
742 * array, 0 if not.
743 */
744int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
745 unsigned int low, unsigned int high, char **err)
746{
747 size_t i;
748
749 for (i = 0; i < sz; i++) {
750 if ((low >= rgs[i].low && low <= rgs[i].high) ||
751 (high >= rgs[i].low && high <= rgs[i].high)) {
752 memprintf(err, "ranges are overlapping");
753 return 1;
754 }
755 }
756
757 return 0;
758}
759
760int smp_log_range_cmp(const void *a, const void *b)
761{
762 const struct smp_log_range *rg_a = a;
763 const struct smp_log_range *rg_b = b;
764
765 if (rg_a->high < rg_b->low)
766 return -1;
767 else if (rg_a->low > rg_b->high)
768 return 1;
769
770 return 0;
771}
772
773/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200774 * Parse "log" keyword and update <logsrvs> list accordingly.
775 *
776 * When <do_del> is set, it means the "no log" line was parsed, so all log
777 * servers in <logsrvs> are released.
778 *
779 * Otherwise, we try to parse the "log" line. First of all, when the list is not
780 * the global one, we look for the parameter "global". If we find it,
781 * global.logsrvs is copied. Else we parse each arguments.
782 *
783 * The function returns 1 in success case, otherwise, it returns 0 and err is
784 * filled.
785 */
Emeric Brun9533a702021-04-02 10:13:43 +0200786int parse_logsrv(char **args, struct list *logsrvs, int do_del, const char *file, int linenum, char **err)
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200787{
Willy Tarreauae32ac72020-10-27 09:51:37 +0100788 struct smp_log_range *smp_rgs = NULL;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200789 struct sockaddr_storage *sk;
Emeric Brun94aab062021-04-02 10:41:36 +0200790 struct protocol *proto;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200791 struct logsrv *logsrv = NULL;
792 int port1, port2;
793 int cur_arg;
Willy Tarreau89599262020-09-15 14:03:26 +0200794 int fd;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200795
796 /*
797 * "no log": delete previous herited or defined syslog
798 * servers.
799 */
800 if (do_del) {
801 struct logsrv *back;
802
803 if (*(args[1]) != 0) {
804 memprintf(err, "'no log' does not expect arguments");
805 goto error;
806 }
807
808 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200809 LIST_DELETE(&logsrv->list);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200810 free(logsrv);
811 }
812 return 1;
813 }
814
815 /*
816 * "log global": copy global.logrsvs linked list to the end of logsrvs
817 * list. But first, we check (logsrvs != global.logsrvs).
818 */
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100819 if (*(args[1]) && *(args[2]) == 0 && strcmp(args[1], "global") == 0) {
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200820 if (logsrvs == &global.logsrvs) {
821 memprintf(err, "'global' is not supported for a global syslog server");
822 goto error;
823 }
824 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200825 struct logsrv *node;
826
827 list_for_each_entry(node, logsrvs, list) {
828 if (node->ref == logsrv)
829 goto skip_logsrv;
830 }
831
832 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200833 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200834 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200835 LIST_INIT(&node->list);
Willy Tarreau2b718102021-04-21 07:32:39 +0200836 LIST_APPEND(logsrvs, &node->list);
Emeric Brun9533a702021-04-02 10:13:43 +0200837 node->conf.file = strdup(file);
838 node->conf.line = linenum;
Christopher Faulet28ac0992018-03-26 16:09:19 +0200839
840 skip_logsrv:
841 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200842 }
843 return 1;
844 }
845
846 /*
847 * "log <address> ...: parse a syslog server line
848 */
849 if (*(args[1]) == 0 || *(args[2]) == 0) {
850 memprintf(err, "expects <address> and <facility> %s as arguments",
851 ((logsrvs == &global.logsrvs) ? "" : "or global"));
852 goto error;
853 }
854
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100855 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
856 if (strcmp(args[1], "stdout") == 0)
857 args[1] = "fd@1";
858 else if (strcmp(args[1], "stderr") == 0)
859 args[1] = "fd@2";
860
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200861 logsrv = calloc(1, sizeof(*logsrv));
862 if (!logsrv) {
863 memprintf(err, "out of memory");
864 goto error;
865 }
866
Emeric Brun9533a702021-04-02 10:13:43 +0200867 logsrv->conf.file = strdup(file);
868 logsrv->conf.line = linenum;
869
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200870 /* skip address for now, it will be parsed at the end */
871 cur_arg = 2;
872
873 /* just after the address, a length may be specified */
874 logsrv->maxlen = MAX_SYSLOG_LEN;
875 if (strcmp(args[cur_arg], "len") == 0) {
876 int len = atoi(args[cur_arg+1]);
877 if (len < 80 || len > 65535) {
878 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
879 args[cur_arg+1]);
880 goto error;
881 }
882 logsrv->maxlen = len;
883 cur_arg += 2;
884 }
885 if (logsrv->maxlen > global.max_syslog_len)
886 global.max_syslog_len = logsrv->maxlen;
887
888 /* after the length, a format may be specified */
889 if (strcmp(args[cur_arg], "format") == 0) {
890 logsrv->format = get_log_format(args[cur_arg+1]);
Emeric Brun54648852020-07-06 15:54:06 +0200891 if (logsrv->format == LOG_FORMAT_UNSPEC) {
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200892 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
893 goto error;
894 }
895 cur_arg += 2;
896 }
897
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200898 if (strcmp(args[cur_arg], "sample") == 0) {
899 unsigned low, high;
900 char *p, *beg, *end, *smp_sz_str;
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200901 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
902
903 p = args[cur_arg+1];
904 smp_sz_str = strchr(p, ':');
905 if (!smp_sz_str) {
906 memprintf(err, "Missing sample size");
907 goto error;
908 }
909
910 *smp_sz_str++ = '\0';
911
912 end = p + strlen(p);
913
914 while (p != end) {
915 if (!get_logsrv_smp_range(&low, &high, &p, err))
916 goto error;
917
918 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
919 goto error;
920
921 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
922 if (!smp_rgs) {
923 memprintf(err, "out of memory error");
924 goto error;
925 }
926
927 smp_rgs[smp_rgs_sz].low = low;
928 smp_rgs[smp_rgs_sz].high = high;
929 smp_rgs[smp_rgs_sz].sz = high - low + 1;
930 smp_rgs[smp_rgs_sz].curr_idx = 0;
931 if (smp_rgs[smp_rgs_sz].high > smp_sz)
932 smp_sz = smp_rgs[smp_rgs_sz].high;
933 smp_rgs_sz++;
934 }
935
Tim Duesterhus21648002019-06-23 22:10:10 +0200936 if (smp_rgs == NULL) {
937 memprintf(err, "no sampling ranges given");
938 goto error;
939 }
940
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200941 beg = smp_sz_str;
942 end = beg + strlen(beg);
943 new_smp_sz = read_uint((const char **)&beg, end);
944 if (!new_smp_sz || beg != end) {
945 memprintf(err, "wrong sample size '%s' for sample range '%s'",
946 smp_sz_str, args[cur_arg+1]);
947 goto error;
948 }
949
950 if (new_smp_sz < smp_sz) {
951 memprintf(err, "sample size %zu should be greater or equal to "
952 "%zu the maximum of the high ranges limits",
953 new_smp_sz, smp_sz);
954 goto error;
955 }
956 smp_sz = new_smp_sz;
957
958 /* Let's order <smp_rgs> array. */
959 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
960
961 logsrv->lb.smp_rgs = smp_rgs;
962 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
963 logsrv->lb.smp_sz = smp_sz;
964
965 cur_arg += 2;
966 }
Frédéric Lécailled803e472019-04-25 07:42:09 +0200967 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200968 /* parse the facility */
969 logsrv->facility = get_log_facility(args[cur_arg]);
970 if (logsrv->facility < 0) {
971 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
972 goto error;
973 }
974 cur_arg++;
975
976 /* parse the max syslog level (default: debug) */
977 logsrv->level = 7;
978 if (*(args[cur_arg])) {
979 logsrv->level = get_log_level(args[cur_arg]);
980 if (logsrv->level < 0) {
981 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
982 goto error;
983 }
984 cur_arg++;
985 }
986
987 /* parse the limit syslog level (default: emerg) */
988 logsrv->minlvl = 0;
989 if (*(args[cur_arg])) {
990 logsrv->minlvl = get_log_level(args[cur_arg]);
991 if (logsrv->minlvl < 0) {
992 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
993 goto error;
994 }
995 cur_arg++;
996 }
997
998 /* Too many args */
999 if (*(args[cur_arg])) {
1000 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
1001 goto error;
1002 }
1003
1004 /* now, back to the address */
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001005 logsrv->type = LOG_TARGET_DGRAM;
Willy Tarreauc046d162019-08-30 15:24:59 +02001006 if (strncmp(args[1], "ring@", 5) == 0) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001007 logsrv->addr.ss_family = AF_UNSPEC;
1008 logsrv->type = LOG_TARGET_BUFFER;
Emeric Brun99c453d2020-05-25 15:01:04 +02001009 logsrv->sink = NULL;
1010 logsrv->ring_name = strdup(args[1] + 5);
Willy Tarreauc046d162019-08-30 15:24:59 +02001011 goto done;
1012 }
1013
Emeric Brun94aab062021-04-02 10:41:36 +02001014 sk = str2sa_range(args[1], NULL, &port1, &port2, &fd, &proto,
1015 err, NULL, NULL,
1016 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_RAW_FD | PA_O_DGRAM | PA_O_STREAM | PA_O_DEFAULT_DGRAM);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001017 if (!sk)
1018 goto error;
Willy Tarreau89599262020-09-15 14:03:26 +02001019
1020 if (fd != -1)
1021 logsrv->type = LOG_TARGET_FD;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001022 logsrv->addr = *sk;
1023
1024 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001025 if (!port1)
1026 set_host_port(&logsrv->addr, SYSLOG_PORT);
1027 }
Emeric Brun9533a702021-04-02 10:13:43 +02001028
Emeric Brun26754902021-04-07 14:26:44 +02001029 if (proto && proto->ctrl_type == SOCK_STREAM) {
Emeric Brun94aab062021-04-02 10:41:36 +02001030 static unsigned long ring_ids;
1031
1032 /* Implicit sink buffer will be
1033 * initialized in post_check
1034 */
1035 logsrv->type = LOG_TARGET_BUFFER;
1036 logsrv->sink = NULL;
1037 /* compute uniq name for the ring */
1038 memprintf(&logsrv->ring_name, "ring#%lu", ++ring_ids);
1039 }
1040
Willy Tarreauc046d162019-08-30 15:24:59 +02001041 done:
Willy Tarreau2b718102021-04-21 07:32:39 +02001042 LIST_APPEND(logsrvs, &logsrv->list);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001043 return 1;
1044
1045 error:
Willy Tarreauae32ac72020-10-27 09:51:37 +01001046 free(smp_rgs);
Emeric Brun9533a702021-04-02 10:13:43 +02001047 if (logsrv) {
1048 free(logsrv->conf.file);
Willy Tarreaua0133fc2020-10-27 10:35:32 +01001049 free(logsrv->ring_name);
Emeric Brun9533a702021-04-02 10:13:43 +02001050 }
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001051 free(logsrv);
1052 return 0;
1053}
1054
Amaury Denoyelle7b01a8d2021-03-29 10:29:07 +02001055
1056/*
Emeric Brun54648852020-07-06 15:54:06 +02001057 * returns log format, LOG_FORMAT_UNSPEC is return if not found.
Dragan Dosen1322d092015-09-22 16:05:32 +02001058 */
Emeric Brun54648852020-07-06 15:54:06 +02001059enum log_fmt get_log_format(const char *fmt)
Dragan Dosen1322d092015-09-22 16:05:32 +02001060{
Emeric Brun54648852020-07-06 15:54:06 +02001061 enum log_fmt format;
Dragan Dosen1322d092015-09-22 16:05:32 +02001062
1063 format = LOG_FORMATS - 1;
Emeric Brun54648852020-07-06 15:54:06 +02001064 while (format > 0 && log_formats[format].name
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001065 && strcmp(log_formats[format].name, fmt) != 0)
Dragan Dosen1322d092015-09-22 16:05:32 +02001066 format--;
1067
Emeric Brun54648852020-07-06 15:54:06 +02001068 /* Note: 0 is LOG_FORMAT_UNSPEC */
Dragan Dosen1322d092015-09-22 16:05:32 +02001069 return format;
1070}
1071
1072/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001073 * returns log level for <lev> or -1 if not found.
1074 */
1075int get_log_level(const char *lev)
1076{
1077 int level;
1078
1079 level = NB_LOG_LEVELS - 1;
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001080 while (level >= 0 && strcmp(log_levels[level], lev) != 0)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001081 level--;
1082
1083 return level;
1084}
1085
Willy Tarreaubaaee002006-06-26 02:48:02 +02001086/*
1087 * returns log facility for <fac> or -1 if not found.
1088 */
1089int get_log_facility(const char *fac)
1090{
1091 int facility;
1092
1093 facility = NB_LOG_FACILITIES - 1;
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001094 while (facility >= 0 && strcmp(log_facilities[facility], fac) != 0)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001095 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001096
Willy Tarreaubaaee002006-06-26 02:48:02 +02001097 return facility;
1098}
1099
William Lallemanda1cc3812012-02-08 16:38:44 +01001100/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001101 * Encode the string.
1102 *
1103 * When using the +E log format option, it will try to escape '"\]'
1104 * characters with '\' as prefix. The same prefix should not be used as
1105 * <escape>.
1106 */
1107static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001108 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001109 const char *string,
1110 struct logformat_node *node)
1111{
1112 if (node->options & LOG_OPT_ESC) {
1113 if (start < stop) {
1114 stop--; /* reserve one byte for the final '\0' */
1115 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001116 if (!ha_bit_test((unsigned char)(*string), map)) {
1117 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001118 *start++ = *string;
1119 else {
1120 if (start + 2 >= stop)
1121 break;
1122 *start++ = '\\';
1123 *start++ = *string;
1124 }
1125 }
1126 else {
1127 if (start + 3 >= stop)
1128 break;
1129 *start++ = escape;
1130 *start++ = hextab[(*string >> 4) & 15];
1131 *start++ = hextab[*string & 15];
1132 }
1133 string++;
1134 }
1135 *start = '\0';
1136 }
1137 }
1138 else {
1139 return encode_string(start, stop, escape, map, string);
1140 }
1141
1142 return start;
1143}
1144
1145/*
1146 * Encode the chunk.
1147 *
1148 * When using the +E log format option, it will try to escape '"\]'
1149 * characters with '\' as prefix. The same prefix should not be used as
1150 * <escape>.
1151 */
1152static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001153 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001154 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001155 struct logformat_node *node)
1156{
1157 char *str, *end;
1158
1159 if (node->options & LOG_OPT_ESC) {
1160 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001161 str = chunk->area;
1162 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001163
1164 stop--; /* reserve one byte for the final '\0' */
1165 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001166 if (!ha_bit_test((unsigned char)(*str), map)) {
1167 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001168 *start++ = *str;
1169 else {
1170 if (start + 2 >= stop)
1171 break;
1172 *start++ = '\\';
1173 *start++ = *str;
1174 }
1175 }
1176 else {
1177 if (start + 3 >= stop)
1178 break;
1179 *start++ = escape;
1180 *start++ = hextab[(*str >> 4) & 15];
1181 *start++ = hextab[*str & 15];
1182 }
1183 str++;
1184 }
1185 *start = '\0';
1186 }
1187 }
1188 else {
1189 return encode_chunk(start, stop, escape, map, chunk);
1190 }
1191
1192 return start;
1193}
1194
1195/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001196 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001197 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001198 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001199 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001200 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001201char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const struct logformat_node *node)
William Lallemanda1cc3812012-02-08 16:38:44 +01001202{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001203 if (size < 2)
1204 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001205
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001206 if (node->options & LOG_OPT_QUOTE) {
1207 *(dst++) = '"';
1208 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001209 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001210
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001211 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001212 if (++len > size)
1213 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001214 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001215 char *ret;
1216
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001217 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001218 if (ret == NULL || *ret != '\0')
1219 return NULL;
1220 len = ret - dst;
1221 }
1222 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001223 len = strlcpy2(dst, src, len);
1224 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001225
1226 size -= len;
1227 dst += len;
1228 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001229 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1230 if (size < 2)
1231 return NULL;
1232 *(dst++) = '-';
1233 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001234
1235 if (node->options & LOG_OPT_QUOTE) {
1236 if (size < 2)
1237 return NULL;
1238 *(dst++) = '"';
1239 }
1240
1241 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001242 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001243}
1244
Willy Tarreau26ffa852018-09-05 15:23:10 +02001245static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001246{
1247 return lf_text_len(dst, src, size, size, node);
1248}
1249
William Lallemand5f232402012-04-05 18:02:55 +02001250/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001251 * Write a IP address to the log string
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001252 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001253 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001254char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001255{
1256 char *ret = dst;
1257 int iret;
1258 char pn[INET6_ADDRSTRLEN];
1259
1260 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001261 unsigned char *addr = NULL;
1262 switch (sockaddr->sa_family) {
1263 case AF_INET:
1264 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1265 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1266 break;
1267 case AF_INET6:
1268 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1269 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1270 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1271 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1272 break;
1273 default:
1274 return NULL;
1275 }
William Lallemand5f232402012-04-05 18:02:55 +02001276 if (iret < 0 || iret > size)
1277 return NULL;
1278 ret += iret;
1279 } else {
1280 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1281 ret = lf_text(dst, pn, size, node);
1282 if (ret == NULL)
1283 return NULL;
1284 }
1285 return ret;
1286}
1287
1288/*
1289 * Write a port to the log
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001290 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001291 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001292char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001293{
1294 char *ret = dst;
1295 int iret;
1296
1297 if (node->options & LOG_OPT_HEXA) {
1298 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1299 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1300 if (iret < 0 || iret > size)
1301 return NULL;
1302 ret += iret;
1303 } else {
1304 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1305 if (ret == NULL)
1306 return NULL;
1307 }
1308 return ret;
1309}
1310
Emeric Brun54648852020-07-06 15:54:06 +02001311
1312/*
1313 * This function sends the syslog message using a printf format string. It
1314 * expects an LF-terminated message.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001315 */
Emeric Brun54648852020-07-06 15:54:06 +02001316void send_log(struct proxy *p, int level, const char *format, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001317{
Emeric Brun54648852020-07-06 15:54:06 +02001318 va_list argp;
1319 int data_len;
1320
1321 if (level < 0 || format == NULL || logline == NULL)
1322 return;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001323
Emeric Brun54648852020-07-06 15:54:06 +02001324 va_start(argp, format);
1325 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
1326 if (data_len < 0 || data_len > global.max_syslog_len)
1327 data_len = global.max_syslog_len;
1328 va_end(argp);
Willy Tarreaufe944602007-10-25 10:34:16 +02001329
Emeric Brun54648852020-07-06 15:54:06 +02001330 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1331 logline, data_len, default_rfc5424_sd_log_format, 2);
1332}
1333/*
1334 * This function builds a log header of given format using given
1335 * metadata, if format is set to LOF_FORMAT_UNSPEC, it tries
1336 * to determine format based on given metadas. It is useful
1337 * for log-forwarding to be able to forward any format without
1338 * settings.
1339 * This function returns a struct ist array of elements of the header
1340 * nbelem is set to the number of available elements.
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001341 * This function returns currently a maximum of NB_LOG_HDR_IST_ELEMENTS
Emeric Brun54648852020-07-06 15:54:06 +02001342 * elements.
1343 */
1344struct ist *build_log_header(enum log_fmt format, int level, int facility,
1345 struct ist *metadata, size_t *nbelem)
1346{
1347 static THREAD_LOCAL struct {
1348 struct ist ist_vector[NB_LOG_HDR_MAX_ELEMENTS];
1349 char timestamp_buffer[LOG_LEGACYTIME_LEN+1+1];
1350 time_t cur_legacy_time;
1351 char priority_buffer[6];
1352 } hdr_ctx = { .priority_buffer = "<<<<>" };
Willy Tarreaubaaee002006-06-26 02:48:02 +02001353
Emeric Brun54648852020-07-06 15:54:06 +02001354 struct tm logtime;
1355 int len;
1356 int fac_level = 0;
1357 time_t time = date.tv_sec;
Dragan Dosen43885c72015-10-01 13:18:13 +02001358
Emeric Brun54648852020-07-06 15:54:06 +02001359 *nbelem = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001360
Emeric Brun54648852020-07-06 15:54:06 +02001361
1362 if (format == LOG_FORMAT_UNSPEC) {
1363 format = LOG_FORMAT_RAW;
1364 if (metadata) {
1365 /* If a hostname is set, it appears we want to perform syslog
1366 * because only rfc5427 or rfc3164 support an hostname.
1367 */
1368 if (metadata[LOG_META_HOST].len) {
1369 /* If a rfc5424 compliant timestamp is used we consider
1370 * that output format is rfc5424, else legacy format
1371 * is used as specified default for local logs
1372 * in documentation.
1373 */
1374 if ((metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-')
1375 || (metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN))
1376 format = LOG_FORMAT_RFC5424;
1377 else
1378 format = LOG_FORMAT_RFC3164;
1379 }
Emeric Brun0237c4e2020-11-27 16:24:34 +01001380 else if (metadata[LOG_META_TAG].len) {
1381 /* Tag is present but no hostname, we should
Ilya Shipitsinf38a0182020-12-21 01:16:17 +05001382 * consider we try to emit a local log
Emeric Brun0237c4e2020-11-27 16:24:34 +01001383 * in legacy format (analog to RFC3164 but
1384 * with stripped hostname).
1385 */
1386 format = LOG_FORMAT_LOCAL;
1387 }
Emeric Brun54648852020-07-06 15:54:06 +02001388 else if (metadata[LOG_META_PRIO].len) {
1389 /* the source seems a parsed message
1390 * offering a valid level/prio prefix
1391 * so we consider this format.
1392 */
1393 format = LOG_FORMAT_PRIO;
1394 }
1395 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001396 }
1397
Emeric Brun54648852020-07-06 15:54:06 +02001398 /* prepare priority, stored into 1 single elem */
1399 switch (format) {
Emeric Brun0237c4e2020-11-27 16:24:34 +01001400 case LOG_FORMAT_LOCAL:
Emeric Brun54648852020-07-06 15:54:06 +02001401 case LOG_FORMAT_RFC3164:
1402 case LOG_FORMAT_RFC5424:
1403 case LOG_FORMAT_PRIO:
1404 fac_level = facility << 3;
1405 /* further format ignore the facility */
1406 /* fall through */
1407 case LOG_FORMAT_TIMED:
1408 case LOG_FORMAT_SHORT:
1409 fac_level += level;
1410 hdr_ctx.ist_vector[*nbelem].ptr = &hdr_ctx.priority_buffer[3]; /* last digit of the log level */
1411 do {
1412 *hdr_ctx.ist_vector[*nbelem].ptr = '0' + fac_level % 10;
1413 fac_level /= 10;
1414 hdr_ctx.ist_vector[*nbelem].ptr--;
1415 } while (fac_level && hdr_ctx.ist_vector[*nbelem].ptr > &hdr_ctx.priority_buffer[0]);
1416 *hdr_ctx.ist_vector[*nbelem].ptr = '<';
1417 hdr_ctx.ist_vector[(*nbelem)++].len = &hdr_ctx.priority_buffer[5] - hdr_ctx.ist_vector[0].ptr;
1418 break;
1419 case LOG_FORMAT_ISO:
1420 case LOG_FORMAT_RAW:
1421 break;
1422 case LOG_FORMAT_UNSPEC:
1423 case LOG_FORMATS:
1424 ABORT_NOW();
1425 }
Willy Tarreau094af4e2015-01-07 15:03:42 +01001426
William Lallemand2a4a44f2012-02-06 16:00:33 +01001427
Emeric Brun54648852020-07-06 15:54:06 +02001428 /* prepare timestamp, stored into a max of 4 elems */
1429 switch (format) {
Emeric Brun0237c4e2020-11-27 16:24:34 +01001430 case LOG_FORMAT_LOCAL:
Emeric Brun54648852020-07-06 15:54:06 +02001431 case LOG_FORMAT_RFC3164:
1432 /* rfc3164 ex: 'Jan 1 00:00:00 ' */
1433 if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
1434 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
1435 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001436 /* time is set, break immediately */
Emeric Brun54648852020-07-06 15:54:06 +02001437 break;
1438 }
1439 else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
1440 int month;
1441 char *timestamp = metadata[LOG_META_TIME].ptr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001442
Emeric Brun54648852020-07-06 15:54:06 +02001443 /* iso time always begins like this: '1970-01-01T00:00:00' */
Dragan Dosen1322d092015-09-22 16:05:32 +02001444
Emeric Brun54648852020-07-06 15:54:06 +02001445 /* compute month */
1446 month = 10*(timestamp[5] - '0') + (timestamp[6] - '0');
1447 if (month)
1448 month--;
1449 if (month <= 11) {
1450 /* builds log prefix ex: 'Jan 1 ' */
1451 len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
1452 "%s %c%c ", monthname[month],
1453 timestamp[8] != '0' ? timestamp[8] : ' ',
1454 timestamp[9]);
1455 /* we reused the timestamp_buffer, signal that it does not
1456 * contain local time anymore
1457 */
1458 hdr_ctx.cur_legacy_time = 0;
1459 if (len == 7) {
1460 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
1461 /* adds 'HH:MM:SS' from iso time */
1462 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[11], 8);
1463 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1464 /* we successfully reuse iso time, we can break */
1465 break;
1466 }
1467 }
1468 /* Failed to reuse isotime time, fallback to local legacy time */
1469 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001470
Emeric Brun54648852020-07-06 15:54:06 +02001471 if (unlikely(time != hdr_ctx.cur_legacy_time)) {
1472 /* re-builds timestamp from the current local time */
1473 get_localtime(time, &logtime);
1474
1475 len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
1476 "%s %2d %02d:%02d:%02d ",
1477 monthname[logtime.tm_mon],
1478 logtime.tm_mday, logtime.tm_hour, logtime.tm_min, logtime.tm_sec);
1479 if (len != LOG_LEGACYTIME_LEN+1)
1480 hdr_ctx.cur_legacy_time = 0;
1481 else
1482 hdr_ctx.cur_legacy_time = time;
1483 }
1484 if (likely(hdr_ctx.cur_legacy_time))
1485 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], LOG_LEGACYTIME_LEN+1);
1486 else
1487 hdr_ctx.ist_vector[(*nbelem)++] = ist2("Jan 1 00:00:00 ", LOG_LEGACYTIME_LEN+1);
1488 break;
1489 case LOG_FORMAT_RFC5424:
1490 /* adds rfc5425 version prefix */
1491 hdr_ctx.ist_vector[(*nbelem)++] = ist2("1 ", 2);
1492 if (metadata && metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-') {
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001493 /* submitted len is NILVALUE, it is a valid timestamp for rfc5425 */
Emeric Brun54648852020-07-06 15:54:06 +02001494 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
1495 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1496 break;
1497 }
1498 /* let continue as 'timed' and 'iso' format for usual timestamp */
1499 /* fall through */
1500 case LOG_FORMAT_TIMED:
1501 case LOG_FORMAT_ISO:
1502 /* ISO format ex: '1900:01:01T12:00:00.123456Z'
1503 * '1900:01:01T14:00:00+02:00'
1504 * '1900:01:01T10:00:00.123456-02:00'
1505 */
1506 if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
1507 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
1508 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001509 /* time is set, break immediately */
Emeric Brun54648852020-07-06 15:54:06 +02001510 break;
1511 }
1512 else if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
1513 int month;
1514 char *timestamp = metadata[LOG_META_TIME].ptr;
1515
1516 for (month = 0; month < 12; month++)
1517 if (!memcmp(monthname[month], timestamp, 3))
1518 break;
Dragan Dosen1322d092015-09-22 16:05:32 +02001519
Emeric Brun54648852020-07-06 15:54:06 +02001520 if (month < 12) {
1521
1522 /* get local time to retrieve year */
1523 get_localtime(time, &logtime);
1524
1525 /* year seems changed since log */
1526 if (logtime.tm_mon < month)
1527 logtime.tm_year--;
1528
1529 /* builds rfc5424 prefix ex: '1900-01-01T' */
1530 len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
1531 "%4d-%02d-%c%cT",
1532 logtime.tm_year+1900, month+1,
1533 timestamp[4] != ' ' ? timestamp[4] : '0',
1534 timestamp[5]);
1535
1536 /* we reused the timestamp_buffer, signal that it does not
1537 * contain local time anymore
1538 */
1539 hdr_ctx.cur_legacy_time = 0;
1540 if (len == 11) {
1541 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
1542 /* adds HH:MM:SS from legacy timestamp */
1543 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[7], 8);
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001544 /* skip secfraq because it is optional */
Emeric Brun54648852020-07-06 15:54:06 +02001545 /* according to rfc: -00:00 means we don't know the timezone */
1546 hdr_ctx.ist_vector[(*nbelem)++] = ist2("-00:00 ", 7);
1547 /* we successfully reuse legacy time, we can break */
1548 break;
1549 }
1550 }
1551 /* Failed to reuse legacy time, fallback to local iso time */
1552 }
1553 hdr_ctx.ist_vector[(*nbelem)++] = ist2(timeofday_as_iso_us(1), LOG_ISOTIME_MAXLEN + 1);
1554 break;
1555 case LOG_FORMAT_PRIO:
1556 case LOG_FORMAT_SHORT:
1557 case LOG_FORMAT_RAW:
1558 break;
1559 case LOG_FORMAT_UNSPEC:
1560 case LOG_FORMATS:
1561 ABORT_NOW();
Dragan Dosen1322d092015-09-22 16:05:32 +02001562 }
1563
Emeric Brun54648852020-07-06 15:54:06 +02001564 /* prepare other meta data, stored into a max of 10 elems */
1565 switch (format) {
1566 case LOG_FORMAT_RFC3164:
1567 if (metadata && metadata[LOG_META_HOST].len) {
1568 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
1569 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1570 }
Emeric Brun0237c4e2020-11-27 16:24:34 +01001571 else /* the caller MUST fill the hostname, this field is mandatory */
Emeric Brun54648852020-07-06 15:54:06 +02001572 hdr_ctx.ist_vector[(*nbelem)++] = ist2("localhost ", 10);
Emeric Brun0237c4e2020-11-27 16:24:34 +01001573 /* fall through */
1574 case LOG_FORMAT_LOCAL:
Emeric Brun54648852020-07-06 15:54:06 +02001575 if (!metadata || !metadata[LOG_META_TAG].len)
1576 break;
Dragan Dosen1322d092015-09-22 16:05:32 +02001577
Emeric Brun54648852020-07-06 15:54:06 +02001578 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
1579 if (metadata[LOG_META_PID].len) {
1580 hdr_ctx.ist_vector[(*nbelem)++] = ist2("[", 1);
1581 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
1582 hdr_ctx.ist_vector[(*nbelem)++] = ist2("]", 1);
1583 }
1584 hdr_ctx.ist_vector[(*nbelem)++] = ist2(": ", 2);
1585 break;
1586 case LOG_FORMAT_RFC5424:
1587 if (metadata && metadata[LOG_META_HOST].len) {
1588 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
1589 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1590 }
1591 else
1592 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
Dragan Dosen1322d092015-09-22 16:05:32 +02001593
Emeric Brun54648852020-07-06 15:54:06 +02001594 if (metadata && metadata[LOG_META_TAG].len) {
1595 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
1596 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1597 }
1598 else
1599 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001600
Emeric Brun54648852020-07-06 15:54:06 +02001601 if (metadata && metadata[LOG_META_PID].len) {
1602 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
1603 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1604 }
1605 else
1606 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001607
Emeric Brun54648852020-07-06 15:54:06 +02001608 if (metadata && metadata[LOG_META_MSGID].len) {
1609 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_MSGID];
1610 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1611 }
1612 else
1613 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001614
Emeric Brun54648852020-07-06 15:54:06 +02001615 if (metadata && metadata[LOG_META_STDATA].len) {
1616 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_STDATA];
1617 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1618 }
1619 else
1620 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
1621 break;
1622 case LOG_FORMAT_PRIO:
1623 case LOG_FORMAT_SHORT:
1624 case LOG_FORMAT_TIMED:
1625 case LOG_FORMAT_ISO:
1626 case LOG_FORMAT_RAW:
1627 break;
1628 case LOG_FORMAT_UNSPEC:
1629 case LOG_FORMATS:
1630 ABORT_NOW();
1631 }
1632
1633 return hdr_ctx.ist_vector;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001634}
1635
1636/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001637 * This function sends a syslog message to <logsrv>.
Emeric Brun54648852020-07-06 15:54:06 +02001638 * The argument <metadata> MUST be an array of size
1639 * LOG_META_FIELDS*sizeof(struct ist) containing data to build the header.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001640 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001641 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001642 */
Emeric Brun54648852020-07-06 15:54:06 +02001643static inline void __do_send_log(struct logsrv *logsrv, int nblogger, int level, int facility, struct ist *metadata, char *message, size_t size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001644{
Emeric Brun54648852020-07-06 15:54:06 +02001645 static THREAD_LOCAL struct iovec iovec[NB_LOG_HDR_MAX_ELEMENTS+1+1] = { }; /* header elements + message + LF */
Christopher Fauletf8188c62017-06-02 16:20:16 +02001646 static THREAD_LOCAL struct msghdr msghdr = {
1647 //.msg_iov = iovec,
Emeric Brun54648852020-07-06 15:54:06 +02001648 .msg_iovlen = NB_LOG_HDR_MAX_ELEMENTS+2
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001649 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001650 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1651 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001652 int *plogfd;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001653 int sent;
Emeric Brun54648852020-07-06 15:54:06 +02001654 size_t nbelem;
1655 struct ist *msg_header = NULL;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001656
1657 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001658
Emeric Brunfa9d7802020-05-28 14:21:33 +02001659 /* historically some messages used to already contain the trailing LF
1660 * or Zero. Let's remove all trailing LF or Zero
1661 */
Emeric Brun54648852020-07-06 15:54:06 +02001662 while (size && (message[size-1] == '\n' || (message[size-1] == 0)))
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001663 size--;
1664
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001665 if (logsrv->type == LOG_TARGET_BUFFER) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001666 plogfd = NULL;
Emeric Brune709e1e2020-05-06 17:23:59 +02001667 goto send;
Willy Tarreauc046d162019-08-30 15:24:59 +02001668 }
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001669 else if (logsrv->addr.ss_family == AF_CUST_EXISTING_FD) {
1670 /* the socket's address is a file descriptor */
1671 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1672 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001673 else if (logsrv->addr.ss_family == AF_UNIX)
1674 plogfd = &logfdunix;
1675 else
1676 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001677
Willy Tarreauc046d162019-08-30 15:24:59 +02001678 if (plogfd && unlikely(*plogfd < 0)) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001679 /* socket not successfully initialized yet */
1680 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1681 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1682 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001683
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001684 if (!once) {
1685 once = 1; /* note: no need for atomic ops here */
1686 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1687 nblogger, strerror(errno), errno);
1688 }
1689 return;
1690 } else {
1691 /* we don't want to receive anything on this socket */
1692 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1693 /* does nothing under Linux, maybe needed for others */
1694 shutdown(*plogfd, SHUT_RD);
1695 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1696 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001697 }
1698
Emeric Brun54648852020-07-06 15:54:06 +02001699 msg_header = build_log_header(logsrv->format, level, facility, metadata, &nbelem);
1700 send:
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001701 if (logsrv->type == LOG_TARGET_BUFFER) {
Emeric Brun54648852020-07-06 15:54:06 +02001702 struct ist msg;
1703
1704 msg = ist2(message, size);
1705 if (msg.len > logsrv->maxlen)
1706 msg.len = logsrv->maxlen;
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001707
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001708 sent = sink_write(logsrv->sink, &msg, 1, level, logsrv->facility, metadata);
1709 }
1710 else if (logsrv->addr.ss_family == AF_CUST_EXISTING_FD) {
1711 struct ist msg;
1712
1713 msg = ist2(message, size);
1714 if (msg.len > logsrv->maxlen)
1715 msg.len = logsrv->maxlen;
1716
1717 sent = fd_write_frag_line(*plogfd, logsrv->maxlen, msg_header, nbelem, &msg, 1, 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001718 }
1719 else {
Emeric Brun54648852020-07-06 15:54:06 +02001720 int i = 0;
1721 int totlen = logsrv->maxlen;
1722
1723 for (i = 0 ; i < nbelem ; i++ ) {
1724 iovec[i].iov_base = msg_header[i].ptr;
1725 iovec[i].iov_len = msg_header[i].len;
1726 if (totlen <= iovec[i].iov_len) {
1727 iovec[i].iov_len = totlen;
1728 totlen = 0;
1729 break;
1730 }
1731 totlen -= iovec[i].iov_len;
1732 }
1733 if (totlen) {
1734 iovec[i].iov_base = message;
1735 iovec[i].iov_len = size;
1736 if (totlen <= iovec[i].iov_len)
1737 iovec[i].iov_len = totlen;
1738 i++;
1739 }
1740 iovec[i].iov_base = "\n"; /* insert a \n at the end of the message */
1741 iovec[i].iov_len = 1;
1742 i++;
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001743
Emeric Brun54648852020-07-06 15:54:06 +02001744 msghdr.msg_iovlen = i;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001745 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1746 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001747
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001748 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1749 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001750
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001751 if (sent < 0) {
1752 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001753
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001754 if (errno == EAGAIN)
Willy Tarreau4781b152021-04-06 13:53:36 +02001755 _HA_ATOMIC_INC(&dropped_logs);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001756 else if (!once) {
1757 once = 1; /* note: no need for atomic ops here */
1758 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1759 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001760 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001761 }
1762}
Dragan Dosen59cee972015-09-19 22:09:02 +02001763
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001764/*
1765 * This function sends a syslog message.
1766 * It doesn't care about errors nor does it report them.
Emeric Brun54648852020-07-06 15:54:06 +02001767 * The argument <metadata> MUST be an array of size
1768 * LOG_META_FIELDS*sizeof(struct ist) containing
1769 * data to build the header.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001770 */
Emeric Brun54648852020-07-06 15:54:06 +02001771void process_send_log(struct list *logsrvs, int level, int facility,
1772 struct ist *metadata, char *message, size_t size)
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001773{
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001774 struct logsrv *logsrv;
1775 int nblogger;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001776
1777 /* Send log messages to syslog server. */
1778 nblogger = 0;
1779 list_for_each_entry(logsrv, logsrvs, list) {
Emeric Brun2f4cc282020-07-10 15:47:11 +02001780 int in_range = 1;
Frédéric Lécailled803e472019-04-25 07:42:09 +02001781
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001782 /* we can filter the level of the messages that are sent to each logger */
1783 if (level > logsrv->level)
1784 continue;
1785
Frédéric Lécailled803e472019-04-25 07:42:09 +02001786 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)
Emeric Brun54648852020-07-06 15:54:06 +02001804 __do_send_log(logsrv, ++nblogger, MAX(level, logsrv->minlvl),
1805 (facility == -1) ? logsrv->facility : facility,
1806 metadata, message, size);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001807 }
1808}
1809
Emeric Brun54648852020-07-06 15:54:06 +02001810/*
1811 * This function sends a syslog message.
1812 * It doesn't care about errors nor does it report them.
1813 * The arguments <sd> and <sd_size> are used for the structured-data part
1814 * in RFC5424 formatted syslog messages.
1815 */
1816void __send_log(struct list *logsrvs, struct buffer *tagb, int level,
1817 char *message, size_t size, char *sd, size_t sd_size)
1818{
1819 static THREAD_LOCAL pid_t curr_pid;
1820 static THREAD_LOCAL char pidstr[16];
1821 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
1822
1823 if (logsrvs == NULL) {
1824 if (!LIST_ISEMPTY(&global.logsrvs)) {
1825 logsrvs = &global.logsrvs;
1826 }
1827 }
1828 if (!logsrvs || LIST_ISEMPTY(logsrvs))
1829 return;
1830
1831 if (!metadata[LOG_META_HOST].len) {
1832 if (global.log_send_hostname)
1833 metadata[LOG_META_HOST] = ist2(global.log_send_hostname, strlen(global.log_send_hostname));
Emeric Brun54648852020-07-06 15:54:06 +02001834 }
1835
1836 if (!tagb || !tagb->area)
1837 tagb = &global.log_tag;
1838
1839 if (tagb)
1840 metadata[LOG_META_TAG] = ist2(tagb->area, tagb->data);
1841
1842 if (unlikely(curr_pid != getpid()))
1843 metadata[LOG_META_PID].len = 0;
1844
1845 if (!metadata[LOG_META_PID].len) {
1846 curr_pid = getpid();
1847 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1848 metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
1849 }
1850
1851 metadata[LOG_META_STDATA] = ist2(sd, sd_size);
1852
1853 /* Remove trailing space of structured data */
1854 while (metadata[LOG_META_STDATA].len && metadata[LOG_META_STDATA].ptr[metadata[LOG_META_STDATA].len-1] == ' ')
1855 metadata[LOG_META_STDATA].len--;
1856
1857 return process_send_log(logsrvs, level, -1, metadata, message, size);
1858}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001859
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001860const char sess_cookie[8] = "NIDVEOU7"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001861const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1862 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1863 Set-cookie Updated, unknown, unknown */
1864
William Lallemand1d705562012-03-12 12:46:41 +01001865/*
1866 * try to write a character if there is enough space, or goto out
1867 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001868#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001869 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001870 *(tmplog++) = (x); \
1871 } else { \
1872 goto out; \
1873 } \
1874 } while(0)
1875
Dragan Dosen835b9212016-02-12 13:23:03 +01001876
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001877/* Initializes some log data at boot */
1878static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001879{
1880 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001881 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001882
1883 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1884 * inside PARAM-VALUE should be escaped with '\' as prefix.
1885 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1886 * details.
1887 */
1888 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1889
1890 tmp = "\"\\]";
1891 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001892 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001893 tmp++;
1894 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001895
1896 /* initialize the log header encoding map : '{|}"#' should be encoded with
1897 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1898 * URL encoding only requires '"', '#' to be encoded as well as non-
1899 * printable characters above.
1900 */
1901 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1902 memset(url_encode_map, 0, sizeof(url_encode_map));
1903 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001904 ha_bit_set(i, hdr_encode_map);
1905 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001906 }
1907 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001908 ha_bit_set(i, hdr_encode_map);
1909 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001910 }
1911
1912 tmp = "\"#{|}";
1913 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001914 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001915 tmp++;
1916 }
1917
1918 tmp = "\"#";
1919 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001920 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001921 tmp++;
1922 }
1923
1924 /* initialize the http header encoding map. The draft httpbis define the
1925 * header content as:
1926 *
1927 * HTTP-message = start-line
1928 * *( header-field CRLF )
1929 * CRLF
1930 * [ message-body ]
1931 * header-field = field-name ":" OWS field-value OWS
1932 * field-value = *( field-content / obs-fold )
1933 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1934 * obs-fold = CRLF 1*( SP / HTAB )
1935 * field-vchar = VCHAR / obs-text
1936 * VCHAR = %x21-7E
1937 * obs-text = %x80-FF
1938 *
1939 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1940 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001941 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001942 */
1943 memset(http_encode_map, 0, sizeof(http_encode_map));
1944 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001945 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001946 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001947 ha_bit_set(i, http_encode_map);
1948 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001949}
William Lallemand1d705562012-03-12 12:46:41 +01001950
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001951INITCALL0(STG_PREPARE, init_log);
1952
Christopher Faulet0132d062017-07-26 15:33:35 +02001953/* Initialize log buffers used for syslog messages */
1954int init_log_buffers()
1955{
Christopher Faulet0132d062017-07-26 15:33:35 +02001956 logline = my_realloc2(logline, global.max_syslog_len + 1);
1957 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
Emeric Brun54648852020-07-06 15:54:06 +02001958 if (!logline || !logline_rfc5424)
Christopher Faulet0132d062017-07-26 15:33:35 +02001959 return 0;
1960 return 1;
1961}
1962
1963/* Deinitialize log buffers used for syslog messages */
1964void deinit_log_buffers()
1965{
Christopher Faulet0132d062017-07-26 15:33:35 +02001966 free(logline);
1967 free(logline_rfc5424);
Christopher Faulet0132d062017-07-26 15:33:35 +02001968 logline = NULL;
1969 logline_rfc5424 = NULL;
1970}
1971
Willy Tarreaudf974472012-12-28 02:44:01 +01001972/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1973 * <maxsize> characters. Returns the size of the output string in characters,
1974 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001975 * is not zero. It requires a valid session and optionally a stream. If the
1976 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001977 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001978int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct list *list_format)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001979{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001980 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001981 struct proxy *be;
1982 struct http_txn *txn;
1983 const struct strm_logs *logs;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02001984 struct connection *fe_conn, *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001985 unsigned int s_flags;
1986 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001987 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001988 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001989 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001990 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001991 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001992 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001993 int t_request;
1994 int hdr;
1995 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001996 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001997 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001998 char *ret;
1999 int iret;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002000 int status;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002001 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002002 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002003 struct strm_logs tmp_strm_log;
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002004 struct ist path;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002005
William Lallemandbddd4fd2012-02-27 11:23:10 +01002006 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02002007
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002008 if (likely(s)) {
2009 be = s->be;
2010 txn = s->txn;
2011 be_conn = cs_conn(objt_cs(s->si[1].end));
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002012 status = (txn ? txn->status : 0);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002013 s_flags = s->flags;
2014 uniq_id = s->uniq_id;
2015 logs = &s->logs;
2016 } else {
2017 /* we have no stream so we first need to initialize a few
2018 * things that are needed later. We do increment the request
2019 * ID so that it's uniquely assigned to this request just as
2020 * if the request had reached the point of being processed.
2021 * A request error is reported as it's the only element we have
2022 * here and which justifies emitting such a log.
2023 */
Christopher Fauletfd818482021-04-14 14:01:41 +02002024 be = ((obj_type(sess->origin) == OBJ_TYPE_CHECK) ? __objt_check(sess->origin)->proxy : fe);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002025 txn = NULL;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002026 fe_conn = objt_conn(sess->origin);
Christopher Fauletfd818482021-04-14 14:01:41 +02002027 be_conn = ((obj_type(sess->origin) == OBJ_TYPE_CHECK) ? cs_conn(__objt_check(sess->origin)->cs) : NULL);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002028 status = 0;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002029 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Willy Tarreau18515722021-04-06 11:57:41 +02002030 uniq_id = _HA_ATOMIC_FETCH_ADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002031
2032 /* prepare a valid log structure */
2033 tmp_strm_log.tv_accept = sess->tv_accept;
2034 tmp_strm_log.accept_date = sess->accept_date;
2035 tmp_strm_log.t_handshake = sess->t_handshake;
Christopher Fauletdd789212020-09-30 15:10:07 +02002036 tmp_strm_log.t_idle = (sess->t_idle >= 0 ? sess->t_idle : 0);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002037 tv_zero(&tmp_strm_log.tv_request);
2038 tmp_strm_log.t_queue = -1;
2039 tmp_strm_log.t_connect = -1;
2040 tmp_strm_log.t_data = -1;
2041 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
2042 tmp_strm_log.bytes_in = 0;
2043 tmp_strm_log.bytes_out = 0;
2044 tmp_strm_log.prx_queue_pos = 0;
2045 tmp_strm_log.srv_queue_pos = 0;
2046
2047 logs = &tmp_strm_log;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002048
2049 if ((fe->mode == PR_MODE_HTTP) && fe_conn && fe_conn->mux && fe_conn->mux->ctl) {
2050 enum mux_exit_status es = fe_conn->mux->ctl(fe_conn, MUX_EXIT_STATUS, NULL);
2051
2052 switch (es) {
2053 case MUX_ES_SUCCESS:
2054 break;
2055 case MUX_ES_INVALID_ERR:
2056 status = 400;
2057 if ((fe_conn->flags & CO_FL_ERROR) || conn_xprt_read0_pending(fe_conn))
2058 s_flags = SF_ERR_CLICL | SF_FINST_R;
2059 else
2060 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
2061 break;
2062 case MUX_ES_TOUT_ERR:
2063 status = 408;
2064 s_flags = SF_ERR_CLITO | SF_FINST_R;
2065 break;
Christopher Faulet142dd332020-12-07 11:24:37 +01002066 case MUX_ES_NOTIMPL_ERR:
2067 status = 501;
2068 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
2069 break;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002070 case MUX_ES_INTERNAL_ERR:
2071 status = 500;
2072 s_flags = SF_ERR_INTERNAL | SF_FINST_R;
2073 break;
2074 default:
2075 break;
2076 }
2077 }
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002078 }
2079
William Lallemandbddd4fd2012-02-27 11:23:10 +01002080 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002081 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2082 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01002083
William Lallemand1d705562012-03-12 12:46:41 +01002084 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02002085
William Lallemandbddd4fd2012-02-27 11:23:10 +01002086 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01002087 if (LIST_ISEMPTY(list_format))
2088 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002089
William Lallemand1d705562012-03-12 12:46:41 +01002090 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002091 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02002092 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01002093 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02002094 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01002095
Willy Tarreauc8368452012-12-21 00:09:23 +01002096 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01002097 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002098 if (!last_isspace) {
2099 LOGCHAR(' ');
2100 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002101 }
2102 break;
2103
William Lallemand1d705562012-03-12 12:46:41 +01002104 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002105 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002106 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002107 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002108 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002109 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002110 last_isspace = 0;
2111 break;
2112
Willy Tarreauc8368452012-12-21 00:09:23 +01002113 case LOG_FMT_EXPR: // sample expression, may be request or response
2114 key = NULL;
Christopher Faulet5f940702020-04-06 10:40:02 +02002115 if (tmp->options & LOG_OPT_REQ_CAP)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002116 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Christopher Faulet5f940702020-04-06 10:40:02 +02002117 if (!key && (tmp->options & LOG_OPT_RES_CAP))
Adis Nezirovic79beb242015-07-06 15:41:02 +02002118 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002119 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002120 ret = lf_encode_chunk(tmplog, dst + maxsize,
2121 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002122 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002123 ret = lf_text_len(tmplog,
2124 key ? key->data.u.str.area : NULL,
2125 key ? key->data.u.str.data : 0,
2126 dst + maxsize - tmplog,
2127 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002128 if (ret == 0)
2129 goto out;
2130 tmplog = ret;
2131 last_isspace = 0;
2132 break;
2133
Willy Tarreau2beef582012-12-20 17:22:52 +01002134 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002135 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002136 if (conn && conn_get_src(conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002137 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002138 else
2139 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002140 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002141 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002142 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002143 last_isspace = 0;
2144 break;
2145
Willy Tarreau2beef582012-12-20 17:22:52 +01002146 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002147 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002148 if (conn && conn_get_src(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002149 if (conn->src->ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002150 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002151 } else {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002152 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002153 dst + maxsize - tmplog, tmp);
2154 }
William Lallemand5f232402012-04-05 18:02:55 +02002155 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002156 else
2157 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2158
William Lallemand5f232402012-04-05 18:02:55 +02002159 if (ret == NULL)
2160 goto out;
2161 tmplog = ret;
2162 last_isspace = 0;
2163 break;
2164
Willy Tarreau2beef582012-12-20 17:22:52 +01002165 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002166 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002167 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002168 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002169 }
2170 else
2171 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2172
William Lallemand1d705562012-03-12 12:46:41 +01002173 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002174 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002175 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002176 last_isspace = 0;
2177 break;
2178
Willy Tarreau2beef582012-12-20 17:22:52 +01002179 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002180 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002181 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002182 if (conn->dst->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002183 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002184 else
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002185 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002186 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002187 else
2188 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2189
William Lallemand5f232402012-04-05 18:02:55 +02002190 if (ret == NULL)
2191 goto out;
2192 tmplog = ret;
2193 last_isspace = 0;
2194 break;
2195
Willy Tarreau2beef582012-12-20 17:22:52 +01002196 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002197 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002198 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002199 else
2200 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2201
William Lallemand1d705562012-03-12 12:46:41 +01002202 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002203 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002204 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002205 last_isspace = 0;
2206 break;
2207
Willy Tarreau2beef582012-12-20 17:22:52 +01002208 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002209 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002210 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002211 else
2212 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2213
William Lallemand5f232402012-04-05 18:02:55 +02002214 if (ret == NULL)
2215 goto out;
2216 tmplog = ret;
2217 last_isspace = 0;
2218 break;
2219
Willy Tarreau2beef582012-12-20 17:22:52 +01002220 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002221 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002222 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002223 else
2224 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2225
William Lallemand5f232402012-04-05 18:02:55 +02002226 if (ret == NULL)
2227 goto out;
2228 tmplog = ret;
2229 last_isspace = 0;
2230 break;
2231
Willy Tarreau2beef582012-12-20 17:22:52 +01002232 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002233 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002234 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002235 else
2236 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2237
William Lallemand1d705562012-03-12 12:46:41 +01002238 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002239 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002240 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002241 last_isspace = 0;
2242 break;
2243
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002244 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002245 get_localtime(logs->accept_date.tv_sec, &tm);
2246 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002247 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002248 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002249 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002250 last_isspace = 0;
2251 break;
2252
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002253 case LOG_FMT_tr: // %tr = start of request date
2254 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002255 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002256 get_localtime(tv.tv_sec, &tm);
2257 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2258 if (ret == NULL)
2259 goto out;
2260 tmplog = ret;
2261 last_isspace = 0;
2262 break;
2263
2264 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002265 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002266 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002267 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002268 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002269 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002270 last_isspace = 0;
2271 break;
2272
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002273 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002274 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002275 get_gmtime(tv.tv_sec, &tm);
2276 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2277 if (ret == NULL)
2278 goto out;
2279 tmplog = ret;
2280 last_isspace = 0;
2281 break;
2282
2283 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002284 get_localtime(logs->accept_date.tv_sec, &tm);
2285 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002286 if (ret == NULL)
2287 goto out;
2288 tmplog = ret;
2289 last_isspace = 0;
2290 break;
2291
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002292 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002293 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002294 get_localtime(tv.tv_sec, &tm);
2295 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2296 if (ret == NULL)
2297 goto out;
2298 tmplog = ret;
2299 last_isspace = 0;
2300 break;
2301
William Lallemand5f232402012-04-05 18:02:55 +02002302 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002303 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002304 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002305 if (iret < 0 || iret > dst + maxsize - tmplog)
2306 goto out;
2307 last_isspace = 0;
2308 tmplog += iret;
2309 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002310 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002311 if (ret == NULL)
2312 goto out;
2313 tmplog = ret;
2314 last_isspace = 0;
2315 }
2316 break;
2317
William Lallemand1d705562012-03-12 12:46:41 +01002318 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002319 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002320 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002321 if (iret < 0 || iret > dst + maxsize - tmplog)
2322 goto out;
2323 last_isspace = 0;
2324 tmplog += iret;
2325 } else {
2326 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002327 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002328 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002329 tmplog, 4);
2330 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002331 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002332 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002333 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002334 }
2335 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002336
William Lallemand1d705562012-03-12 12:46:41 +01002337 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002338 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002339 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002340 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002341 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002342 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002343 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002344 break;
2345
Willy Tarreau773d65f2012-10-12 14:56:11 +02002346 case LOG_FMT_FRONTEND_XPRT: // %ft
2347 src = fe->id;
2348 if (tmp->options & LOG_OPT_QUOTE)
2349 LOGCHAR('"');
2350 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2351 if (iret == 0)
2352 goto out;
2353 tmplog += iret;
Christopher Fauletfd818482021-04-14 14:01:41 +02002354 if (sess->listener && sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002355 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002356 if (tmp->options & LOG_OPT_QUOTE)
2357 LOGCHAR('"');
2358 last_isspace = 0;
2359 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002360#ifdef USE_OPENSSL
2361 case LOG_FMT_SSL_CIPHER: // %sslc
2362 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002363 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002364 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002365 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002366 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002367 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2368 if (ret == NULL)
2369 goto out;
2370 tmplog = ret;
2371 last_isspace = 0;
2372 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002373
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002374 case LOG_FMT_SSL_VERSION: // %sslv
2375 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002376 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002377 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002378 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002379 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002380 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2381 if (ret == NULL)
2382 goto out;
2383 tmplog = ret;
2384 last_isspace = 0;
2385 break;
2386#endif
William Lallemand1d705562012-03-12 12:46:41 +01002387 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002388 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002389 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002390 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002391 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002392 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002393 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002394 break;
2395
William Lallemand1d705562012-03-12 12:46:41 +01002396 case LOG_FMT_SERVER: // %s
Christopher Fauletfd818482021-04-14 14:01:41 +02002397 switch (obj_type(s ? s->target : sess->origin)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002398 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002399 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002400 break;
2401 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002402 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002403 break;
Christopher Fauletfd818482021-04-14 14:01:41 +02002404 case OBJ_TYPE_CHECK:
2405 src = (__objt_check(sess->origin)->server
2406 ? __objt_check(sess->origin)->server->id
2407 : "<NOSRV>");
2408 break;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002409 default:
2410 src = "<NOSRV>";
2411 break;
2412 }
William Lallemand5f232402012-04-05 18:02:55 +02002413 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002414 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002415 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002416 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002417 last_isspace = 0;
2418 break;
2419
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002420 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002421 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002422 if (ret == NULL)
2423 goto out;
2424 tmplog = ret;
2425 last_isspace = 0;
2426 break;
2427
2428 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002429 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002430 if (ret == NULL)
2431 goto out;
2432 tmplog = ret;
2433 last_isspace = 0;
2434 break;
2435
2436 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002437 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002438 tmplog, dst + maxsize - tmplog);
2439 if (ret == NULL)
2440 goto out;
2441 tmplog = ret;
2442 last_isspace = 0;
2443 break;
2444
2445 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002446 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002447 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002448 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002449 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002450 last_isspace = 0;
2451 break;
2452
William Lallemand1d705562012-03-12 12:46:41 +01002453 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002454 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002455 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002456 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002457 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002458 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002459 last_isspace = 0;
2460 break;
2461
William Lallemand1d705562012-03-12 12:46:41 +01002462 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002463 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002464 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002465 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002466 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002467 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002468 last_isspace = 0;
2469 break;
2470
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002471 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002472 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002473 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002474 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002475 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002476 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002477 last_isspace = 0;
2478 break;
2479
Willy Tarreau27b639d2016-05-17 17:55:27 +02002480 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002481 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002482 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002483 tmplog, dst + maxsize - tmplog);
2484 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002485 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002486 tmplog, dst + maxsize - tmplog);
2487 if (ret == NULL)
2488 goto out;
2489 tmplog = ret;
2490 last_isspace = 0;
2491 break;
2492
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002493 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2494 if (!(fe->to_log & LW_BYTES))
2495 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002496 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0),
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002497 tmplog, dst + maxsize - tmplog);
2498 if (ret == NULL)
2499 goto out;
2500 tmplog = ret;
2501 last_isspace = 0;
2502 break;
2503
2504 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002505 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002506 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002507 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002508 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002509 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002510 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002511 last_isspace = 0;
2512 break;
2513
Damien Claisse57c8eb92020-04-28 12:09:19 +00002514 case LOG_FMT_TU: // %Tu = total time seen by user = Tt - Ti
2515 if (!(fe->to_log & LW_BYTES))
2516 LOGCHAR('+');
2517 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0),
2518 tmplog, dst + maxsize - tmplog);
2519 if (ret == NULL)
2520 goto out;
2521 tmplog = ret;
2522 last_isspace = 0;
2523 break;
2524
Willy Tarreau2beef582012-12-20 17:22:52 +01002525 case LOG_FMT_STATUS: // %ST
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002526 ret = ltoa_o(status, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002527 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002528 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002529 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002530 last_isspace = 0;
2531 break;
2532
William Lallemand1d705562012-03-12 12:46:41 +01002533 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002534 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002535 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002536 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002537 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002538 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002539 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002540 last_isspace = 0;
2541 break;
2542
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002543 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002544 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002545 if (ret == NULL)
2546 goto out;
2547 tmplog = ret;
2548 last_isspace = 0;
2549 break;
2550
Willy Tarreau2beef582012-12-20 17:22:52 +01002551 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002552 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002553 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002554 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002555 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002556 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002557 last_isspace = 0;
2558 break;
2559
Willy Tarreau2beef582012-12-20 17:22:52 +01002560 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002561 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002562 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002563 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002564 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002565 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002566 last_isspace = 0;
2567 break;
2568
William Lallemand1d705562012-03-12 12:46:41 +01002569 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002570 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2571 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002572 *tmplog = '\0';
2573 last_isspace = 0;
2574 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002575
William Lallemand1d705562012-03-12 12:46:41 +01002576 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002577 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2578 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002579 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2580 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002581 last_isspace = 0;
2582 break;
2583
William Lallemand1d705562012-03-12 12:46:41 +01002584 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002585 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002586 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002587 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002588 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002589 last_isspace = 0;
2590 break;
2591
William Lallemand1d705562012-03-12 12:46:41 +01002592 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002593 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002594 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002595 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002596 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002597 last_isspace = 0;
2598 break;
2599
William Lallemand1d705562012-03-12 12:46:41 +01002600 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002601 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002602 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002603 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002604 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002605 last_isspace = 0;
2606 break;
2607
William Lallemand1d705562012-03-12 12:46:41 +01002608 case LOG_FMT_SRVCONN: // %sc
Christopher Fauletfd818482021-04-14 14:01:41 +02002609 switch (obj_type(s ? s->target : sess->origin)) {
2610 case OBJ_TYPE_SERVER:
2611 ret = ultoa_o(__objt_server(s->target)->cur_sess,
2612 tmplog, dst + maxsize - tmplog);
2613 break;
2614 case OBJ_TYPE_CHECK:
2615 ret = ultoa_o(__objt_check(sess->origin)->server
2616 ? __objt_check(sess->origin)->server->cur_sess
2617 : 0, tmplog, dst + maxsize - tmplog);
2618 break;
2619 default:
2620 ret = ultoa_o(0, tmplog, dst + maxsize - tmplog);
2621 break;
2622 }
2623
William Lallemand1d705562012-03-12 12:46:41 +01002624 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002625 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002626 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002627 last_isspace = 0;
2628 break;
2629
William Lallemand1d705562012-03-12 12:46:41 +01002630 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002631 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002632 LOGCHAR('+');
Christopher Faulet1d26f222021-04-16 11:24:20 +02002633 ret = ltoa_o(((s && s->si[1].conn_retries > 0)
2634 ? (be->conn_retries - s->si[1].conn_retries)
2635 : ((s && s->si[1].state != SI_ST_INI) ? be->conn_retries : 0)),
2636 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002637 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002638 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002639 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002640 last_isspace = 0;
2641 break;
2642
William Lallemand1d705562012-03-12 12:46:41 +01002643 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002644 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002645 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002646 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002647 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002648 last_isspace = 0;
2649 break;
2650
William Lallemand1d705562012-03-12 12:46:41 +01002651 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002652 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002653 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002654 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002655 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002656 last_isspace = 0;
2657 break;
2658
William Lallemand1d705562012-03-12 12:46:41 +01002659 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002660 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002661 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002662 if (tmp->options & LOG_OPT_QUOTE)
2663 LOGCHAR('"');
2664 LOGCHAR('{');
2665 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2666 if (hdr)
2667 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002668 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002669 ret = lf_encode_string(tmplog, dst + maxsize,
2670 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002671 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002672 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002673 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002674 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002675 }
2676 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002677 if (tmp->options & LOG_OPT_QUOTE)
2678 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002679 last_isspace = 0;
2680 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002681 break;
2682
William Lallemand1d705562012-03-12 12:46:41 +01002683 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002684 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002685 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002686 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2687 if (hdr > 0)
2688 LOGCHAR(' ');
2689 if (tmp->options & LOG_OPT_QUOTE)
2690 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002691 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002692 ret = lf_encode_string(tmplog, dst + maxsize,
2693 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002694 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002695 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002696 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002697 } else if (!(tmp->options & LOG_OPT_QUOTE))
2698 LOGCHAR('-');
2699 if (tmp->options & LOG_OPT_QUOTE)
2700 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002701 last_isspace = 0;
2702 }
2703 }
2704 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002705
William Lallemand1d705562012-03-12 12:46:41 +01002706
2707 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002708 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002709 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002710 if (tmp->options & LOG_OPT_QUOTE)
2711 LOGCHAR('"');
2712 LOGCHAR('{');
2713 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2714 if (hdr)
2715 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002716 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002717 ret = lf_encode_string(tmplog, dst + maxsize,
2718 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002719 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002720 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002721 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002722 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002723 }
2724 LOGCHAR('}');
2725 last_isspace = 0;
2726 if (tmp->options & LOG_OPT_QUOTE)
2727 LOGCHAR('"');
2728 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002729 break;
2730
William Lallemand1d705562012-03-12 12:46:41 +01002731 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002732 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002733 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002734 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2735 if (hdr > 0)
2736 LOGCHAR(' ');
2737 if (tmp->options & LOG_OPT_QUOTE)
2738 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002739 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002740 ret = lf_encode_string(tmplog, dst + maxsize,
2741 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002742 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002743 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002744 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002745 } else if (!(tmp->options & LOG_OPT_QUOTE))
2746 LOGCHAR('-');
2747 if (tmp->options & LOG_OPT_QUOTE)
2748 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002749 last_isspace = 0;
2750 }
2751 }
2752 break;
2753
William Lallemand1d705562012-03-12 12:46:41 +01002754 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002755 /* Request */
2756 if (tmp->options & LOG_OPT_QUOTE)
2757 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002758 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002759 ret = lf_encode_string(tmplog, dst + maxsize,
2760 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002761 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002762 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002763 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002764 if (tmp->options & LOG_OPT_QUOTE)
2765 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002766 last_isspace = 0;
2767 break;
William Lallemand5f232402012-04-05 18:02:55 +02002768
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002769 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002770 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002771
Willy Tarreaub7636d12015-06-17 19:58:02 +02002772 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002773 LOGCHAR('"');
2774
2775 end = uri + strlen(uri);
2776 // look for the first whitespace character
2777 while (uri < end && !HTTP_IS_SPHT(*uri))
2778 uri++;
2779
2780 // keep advancing past multiple spaces
2781 while (uri < end && HTTP_IS_SPHT(*uri)) {
2782 uri++; nspaces++;
2783 }
2784
2785 // look for first space or question mark after url
2786 spc = uri;
2787 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2788 spc++;
2789
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002790 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002791 chunk.area = "<BADREQ>";
2792 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002793 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002794 chunk.area = uri;
2795 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002796 }
2797
Dragan Dosen835b9212016-02-12 13:23:03 +01002798 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002799 if (ret == NULL || *ret != '\0')
2800 goto out;
2801
2802 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002803 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002804 LOGCHAR('"');
2805
2806 last_isspace = 0;
2807 break;
2808
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002809 case LOG_FMT_HTTP_PATH_ONLY: // %HPO
2810 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2811
2812 if (tmp->options & LOG_OPT_QUOTE)
2813 LOGCHAR('"');
2814
2815 end = uri + strlen(uri);
2816
2817 // look for the first whitespace character
2818 while (uri < end && !HTTP_IS_SPHT(*uri))
2819 uri++;
2820
2821 // keep advancing past multiple spaces
2822 while (uri < end && HTTP_IS_SPHT(*uri)) {
2823 uri++; nspaces++;
2824 }
2825
2826 // look for first space after url
2827 spc = uri;
2828 while (spc < end && !HTTP_IS_SPHT(*spc))
2829 spc++;
2830
Tim Duesterhus92c696e2021-02-28 16:11:36 +01002831 path = ist2(uri, spc - uri);
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002832
2833 // extract relative path without query params from url
2834 path = iststop(http_get_path(path), '?');
2835 if (!txn || !txn->uri || nspaces == 0) {
2836 chunk.area = "<BADREQ>";
2837 chunk.data = strlen("<BADREQ>");
2838 } else {
2839 chunk.area = path.ptr;
2840 chunk.data = path.len;
2841 }
2842
2843 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2844 if (ret == NULL || *ret != '\0')
2845 goto out;
2846
2847 tmplog = ret;
2848 if (tmp->options & LOG_OPT_QUOTE)
2849 LOGCHAR('"');
2850
2851 last_isspace = 0;
2852 break;
2853
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002854 case LOG_FMT_HTTP_QUERY: // %HQ
2855 if (tmp->options & LOG_OPT_QUOTE)
2856 LOGCHAR('"');
2857
Willy Tarreau57bc8912016-04-25 17:09:40 +02002858 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002859 chunk.area = "<BADREQ>";
2860 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002861 } else {
2862 uri = txn->uri;
2863 end = uri + strlen(uri);
2864 // look for the first question mark
2865 while (uri < end && *uri != '?')
2866 uri++;
2867
2868 qmark = uri;
2869 // look for first space or question mark after url
2870 while (uri < end && !HTTP_IS_SPHT(*uri))
2871 uri++;
2872
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002873 chunk.area = qmark;
2874 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002875 }
2876
Dragan Dosen835b9212016-02-12 13:23:03 +01002877 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002878 if (ret == NULL || *ret != '\0')
2879 goto out;
2880
2881 tmplog = ret;
2882 if (tmp->options & LOG_OPT_QUOTE)
2883 LOGCHAR('"');
2884
2885 last_isspace = 0;
2886 break;
2887
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002888 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002889 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002890
Willy Tarreaub7636d12015-06-17 19:58:02 +02002891 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002892 LOGCHAR('"');
2893
2894 end = uri + strlen(uri);
2895 // look for the first whitespace character
2896 while (uri < end && !HTTP_IS_SPHT(*uri))
2897 uri++;
2898
2899 // keep advancing past multiple spaces
2900 while (uri < end && HTTP_IS_SPHT(*uri)) {
2901 uri++; nspaces++;
2902 }
2903
2904 // look for first space after url
2905 spc = uri;
2906 while (spc < end && !HTTP_IS_SPHT(*spc))
2907 spc++;
2908
Willy Tarreau57bc8912016-04-25 17:09:40 +02002909 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002910 chunk.area = "<BADREQ>";
2911 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002912 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002913 chunk.area = uri;
2914 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002915 }
2916
Dragan Dosen835b9212016-02-12 13:23:03 +01002917 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002918 if (ret == NULL || *ret != '\0')
2919 goto out;
2920
2921 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002922 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002923 LOGCHAR('"');
2924
2925 last_isspace = 0;
2926 break;
2927
2928 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002929 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002930 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002931 LOGCHAR('"');
2932
2933 end = uri + strlen(uri);
2934 // look for the first whitespace character
2935 spc = uri;
2936 while (spc < end && !HTTP_IS_SPHT(*spc))
2937 spc++;
2938
2939 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002940 chunk.area = "<BADREQ>";
2941 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002942 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002943 chunk.area = uri;
2944 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002945 }
2946
Dragan Dosen835b9212016-02-12 13:23:03 +01002947 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002948 if (ret == NULL || *ret != '\0')
2949 goto out;
2950
2951 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002952 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002953 LOGCHAR('"');
2954
2955 last_isspace = 0;
2956 break;
2957
2958 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002959 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002960 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002961 LOGCHAR('"');
2962
2963 end = uri + strlen(uri);
2964 // look for the first whitespace character
2965 while (uri < end && !HTTP_IS_SPHT(*uri))
2966 uri++;
2967
2968 // keep advancing past multiple spaces
2969 while (uri < end && HTTP_IS_SPHT(*uri)) {
2970 uri++; nspaces++;
2971 }
2972
2973 // look for the next whitespace character
2974 while (uri < end && !HTTP_IS_SPHT(*uri))
2975 uri++;
2976
2977 // keep advancing past multiple spaces
2978 while (uri < end && HTTP_IS_SPHT(*uri))
2979 uri++;
2980
Willy Tarreau57bc8912016-04-25 17:09:40 +02002981 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002982 chunk.area = "<BADREQ>";
2983 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002984 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002985 chunk.area = "HTTP/0.9";
2986 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002987 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002988 chunk.area = uri;
2989 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002990 }
2991
Dragan Dosen835b9212016-02-12 13:23:03 +01002992 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002993 if (ret == NULL || *ret != '\0')
2994 goto out;
2995
2996 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002997 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002998 LOGCHAR('"');
2999
3000 last_isspace = 0;
3001 break;
3002
William Lallemand5f232402012-04-05 18:02:55 +02003003 case LOG_FMT_COUNTER: // %rt
3004 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02003005 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02003006 if (iret < 0 || iret > dst + maxsize - tmplog)
3007 goto out;
3008 last_isspace = 0;
3009 tmplog += iret;
3010 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02003011 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02003012 if (ret == NULL)
3013 goto out;
3014 tmplog = ret;
3015 last_isspace = 0;
3016 }
3017 break;
3018
Willy Tarreau7346acb2014-08-28 15:03:15 +02003019 case LOG_FMT_LOGCNT: // %lc
3020 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003021 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02003022 if (iret < 0 || iret > dst + maxsize - tmplog)
3023 goto out;
3024 last_isspace = 0;
3025 tmplog += iret;
3026 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003027 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02003028 if (ret == NULL)
3029 goto out;
3030 tmplog = ret;
3031 last_isspace = 0;
3032 }
3033 break;
3034
William Lallemand5f232402012-04-05 18:02:55 +02003035 case LOG_FMT_HOSTNAME: // %H
3036 src = hostname;
3037 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
3038 if (ret == NULL)
3039 goto out;
3040 tmplog = ret;
3041 last_isspace = 0;
3042 break;
3043
3044 case LOG_FMT_PID: // %pid
3045 if (tmp->options & LOG_OPT_HEXA) {
3046 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
3047 if (iret < 0 || iret > dst + maxsize - tmplog)
3048 goto out;
3049 last_isspace = 0;
3050 tmplog += iret;
3051 } else {
3052 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
3053 if (ret == NULL)
3054 goto out;
3055 tmplog = ret;
3056 last_isspace = 0;
3057 }
3058 break;
William Lallemanda73203e2012-03-12 12:48:57 +01003059
3060 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003061 ret = NULL;
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003062 if (s)
3063 ret = lf_text_len(tmplog, s->unique_id.ptr, s->unique_id.len, maxsize - (tmplog - dst), tmp);
3064 else
3065 ret = lf_text_len(tmplog, NULL, 0, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01003066 if (ret == NULL)
3067 goto out;
3068 tmplog = ret;
3069 last_isspace = 0;
3070 break;
3071
William Lallemandbddd4fd2012-02-27 11:23:10 +01003072 }
3073 }
3074
3075out:
William Lallemand1d705562012-03-12 12:46:41 +01003076 /* *tmplog is a unused character */
3077 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01003078 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003079
Willy Tarreaubaaee002006-06-26 02:48:02 +02003080}
3081
William Lallemand1d705562012-03-12 12:46:41 +01003082/*
Willy Tarreau87b09662015-04-03 00:22:06 +02003083 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01003084 * Will not log if the frontend has no log defined.
3085 */
Willy Tarreau87b09662015-04-03 00:22:06 +02003086void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01003087{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003088 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01003089 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003090 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01003091
3092 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02003093 err = (s->flags & SF_REDISP) ||
3094 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
3095 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01003096 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02003097 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02003098
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003099 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01003100 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003101
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003102 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01003103 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003104
Willy Tarreauabcd5142013-06-11 17:18:02 +02003105 if (s->logs.level) { /* loglevel was overridden */
3106 if (s->logs.level == -1) {
3107 s->logs.logwait = 0; /* logs disabled */
3108 return;
3109 }
3110 level = s->logs.level - 1;
3111 }
3112 else {
3113 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003114 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02003115 level = LOG_ERR;
3116 }
William Lallemand1d705562012-03-12 12:46:41 +01003117
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003118 /* if unique-id was not generated */
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003119 if (!isttest(s->unique_id) && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Tim Duesterhus2825b4b2020-02-28 15:13:34 +01003120 stream_generate_unique_id(s, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003121 }
3122
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003123 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3124 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
3125 &sess->fe->logformat_sd);
3126 }
3127
Dragan Dosen59cee972015-09-19 22:09:02 +02003128 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01003129 if (size > 0) {
Willy Tarreau4781b152021-04-06 13:53:36 +02003130 _HA_ATOMIC_INC(&sess->fe->log_count);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003131 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3132 logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01003133 s->logs.logwait = 0;
3134 }
3135}
William Lallemandbddd4fd2012-02-27 11:23:10 +01003136
Willy Tarreau53839352018-09-05 19:51:10 +02003137/*
3138 * send a minimalist log for the session. Will not log if the frontend has no
3139 * log defined. It is assumed that this is only used to report anomalies that
3140 * cannot lead to the creation of a regular stream. Because of this the log
3141 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
3142 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003143 * function to report unimportant events. It is safe to call this function with
3144 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02003145 */
3146void sess_log(struct session *sess)
3147{
3148 int size, level;
3149 int sd_size = 0;
3150
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003151 if (!sess)
3152 return;
3153
Willy Tarreau53839352018-09-05 19:51:10 +02003154 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3155 return;
3156
3157 level = LOG_INFO;
3158 if (sess->fe->options2 & PR_O2_LOGERRORS)
3159 level = LOG_ERR;
3160
3161 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3162 sd_size = sess_build_logline(sess, NULL,
3163 logline_rfc5424, global.max_syslog_len,
3164 &sess->fe->logformat_sd);
3165 }
3166
3167 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
3168 if (size > 0) {
Willy Tarreau4781b152021-04-06 13:53:36 +02003169 _HA_ATOMIC_INC(&sess->fe->log_count);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003170 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3171 logline, size + 1, logline_rfc5424, sd_size);
Willy Tarreau53839352018-09-05 19:51:10 +02003172 }
3173}
3174
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003175void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3176{
3177 va_list argp;
3178 int data_len;
3179
3180 if (level < 0 || format == NULL || logline == NULL)
3181 return;
3182
3183 va_start(argp, format);
3184 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3185 if (data_len < 0 || data_len > global.max_syslog_len)
3186 data_len = global.max_syslog_len;
3187 va_end(argp);
3188
3189 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3190}
Emeric Brun54932b42020-07-07 09:43:24 +02003191/*
3192 * This function parse a received log message <buf>, of size <buflen>
3193 * it fills <level>, <facility> and <metadata> depending of the detected
3194 * header format and message will point on remaining payload of <size>
3195 *
3196 * <metadata> must point on a preallocated array of LOG_META_FIELDS*sizeof(struct ist)
3197 * struct ist len will be set to 0 if field is not found
3198 * <level> and <facility> will be set to -1 if not found.
3199 */
3200void parse_log_message(char *buf, size_t buflen, int *level, int *facility,
3201 struct ist *metadata, char **message, size_t *size)
3202{
3203
3204 char *p;
3205 int fac_level = 0;
3206
3207 *level = *facility = -1;
3208
3209 *message = buf;
3210 *size = buflen;
3211
3212 memset(metadata, 0, LOG_META_FIELDS*sizeof(struct ist));
3213
3214 p = buf;
3215 if (*size < 2 || *p != '<')
3216 return;
3217
3218 p++;
3219 while (*p != '>') {
3220 if (*p > '9' || *p < '0')
3221 return;
3222 fac_level = 10*fac_level + (*p - '0');
3223 p++;
3224 if ((p - buf) > buflen)
3225 return;
3226 }
3227
3228 *facility = fac_level >> 3;
3229 *level = fac_level & 0x7;
3230 p++;
3231
3232 metadata[LOG_META_PRIO] = ist2(buf, p - buf);
3233
3234 buflen -= p - buf;
3235 buf = p;
3236
3237 *size = buflen;
3238 *message = buf;
3239
3240 /* for rfc5424, prio is always followed by '1' and ' ' */
3241 if ((*size > 2) && (p[0] == '1') && (p[1] == ' ')) {
3242 /* format is always '1 TIMESTAMP HOSTNAME TAG PID MSGID STDATA '
3243 * followed by message.
3244 * Each header field can present NILVALUE: '-'
3245 */
3246
3247 p += 2;
3248 /* timestamp is NILVALUE '-' */
3249 if (*size > 2 && (p[0] == '-') && p[1] == ' ') {
3250 metadata[LOG_META_TIME] = ist2(p, 1);
3251 p++;
3252 }
3253 else if (*size > LOG_ISOTIME_MINLEN) {
3254 metadata[LOG_META_TIME].ptr = p;
3255
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05003256 /* check if optional secfrac is present
Emeric Brun54932b42020-07-07 09:43:24 +02003257 * in timestamp.
3258 * possible format are:
3259 * ex: '1970-01-01T00:00:00.000000Z'
3260 * '1970-01-01T00:00:00.000000+00:00'
3261 * '1970-01-01T00:00:00.000000-00:00'
3262 * '1970-01-01T00:00:00Z'
3263 * '1970-01-01T00:00:00+00:00'
3264 * '1970-01-01T00:00:00-00:00'
3265 */
3266 p += 19;
3267 if (*p == '.') {
3268 p++;
3269 if ((p - buf) >= buflen)
3270 goto bad_format;
3271 while (*p != 'Z' && *p != '+' && *p != '-') {
3272 if ((unsigned char)(*p - '0') > 9)
3273 goto bad_format;
3274
3275 p++;
3276 if ((p - buf) >= buflen)
3277 goto bad_format;
3278 }
3279 }
3280
3281 if (*p == 'Z')
3282 p++;
3283 else
3284 p += 6; /* case of '+00:00 or '-00:00' */
3285
3286 if ((p - buf) >= buflen || *p != ' ')
3287 goto bad_format;
3288 metadata[LOG_META_TIME].len = p - metadata[LOG_META_TIME].ptr;
3289 }
3290 else
3291 goto bad_format;
3292
3293
3294 p++;
3295 if ((p - buf) >= buflen || *p == ' ')
3296 goto bad_format;
3297
3298 metadata[LOG_META_HOST].ptr = p;
3299 while (*p != ' ') {
3300 p++;
3301 if ((p - buf) >= buflen)
3302 goto bad_format;
3303 }
3304 metadata[LOG_META_HOST].len = p - metadata[LOG_META_HOST].ptr;
3305 if (metadata[LOG_META_HOST].len == 1 && metadata[LOG_META_HOST].ptr[0] == '-')
3306 metadata[LOG_META_HOST].len = 0;
3307
3308 p++;
3309 if ((p - buf) >= buflen || *p == ' ')
3310 goto bad_format;
3311
3312 metadata[LOG_META_TAG].ptr = p;
3313 while (*p != ' ') {
3314 p++;
3315 if ((p - buf) >= buflen)
3316 goto bad_format;
3317 }
3318 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3319 if (metadata[LOG_META_TAG].len == 1 && metadata[LOG_META_TAG].ptr[0] == '-')
3320 metadata[LOG_META_TAG].len = 0;
3321
3322 p++;
3323 if ((p - buf) >= buflen || *p == ' ')
3324 goto bad_format;
3325
3326 metadata[LOG_META_PID].ptr = p;
3327 while (*p != ' ') {
3328 p++;
3329 if ((p - buf) >= buflen)
3330 goto bad_format;
3331 }
3332 metadata[LOG_META_PID].len = p - metadata[LOG_META_PID].ptr;
3333 if (metadata[LOG_META_PID].len == 1 && metadata[LOG_META_PID].ptr[0] == '-')
3334 metadata[LOG_META_PID].len = 0;
3335
3336 p++;
3337 if ((p - buf) >= buflen || *p == ' ')
3338 goto bad_format;
3339
3340 metadata[LOG_META_MSGID].ptr = p;
3341 while (*p != ' ') {
3342 p++;
3343 if ((p - buf) >= buflen)
3344 goto bad_format;
3345 }
3346 metadata[LOG_META_MSGID].len = p - metadata[LOG_META_MSGID].ptr;
3347 if (metadata[LOG_META_MSGID].len == 1 && metadata[LOG_META_MSGID].ptr[0] == '-')
3348 metadata[LOG_META_MSGID].len = 0;
3349
3350 p++;
3351 if ((p - buf) >= buflen || *p == ' ')
3352 goto bad_format;
3353
3354 /* structured data format is:
3355 * ex:
3356 * '[key1=value1 key2=value2][key3=value3]'
3357 *
3358 * space is invalid outside [] because
3359 * considered as the end of structured data field
3360 */
3361 metadata[LOG_META_STDATA].ptr = p;
3362 if (*p == '[') {
3363 int elem = 0;
3364
3365 while (1) {
3366 if (elem) {
3367 /* according to rfc this char is escaped in param values */
3368 if (*p == ']' && *(p-1) != '\\')
3369 elem = 0;
3370 }
3371 else {
3372 if (*p == '[')
3373 elem = 1;
3374 else if (*p == ' ')
3375 break;
3376 else
3377 goto bad_format;
3378 }
3379 p++;
3380 if ((p - buf) >= buflen)
3381 goto bad_format;
3382 }
3383 }
3384 else if (*p == '-') {
3385 /* case of NILVALUE */
3386 p++;
3387 if ((p - buf) >= buflen || *p != ' ')
3388 goto bad_format;
3389 }
3390 else
3391 goto bad_format;
3392
3393 metadata[LOG_META_STDATA].len = p - metadata[LOG_META_STDATA].ptr;
3394 if (metadata[LOG_META_STDATA].len == 1 && metadata[LOG_META_STDATA].ptr[0] == '-')
3395 metadata[LOG_META_STDATA].len = 0;
3396
3397 p++;
3398
3399 buflen -= p - buf;
3400 buf = p;
3401
3402 *size = buflen;
3403 *message = p;
3404 }
3405 else if (*size > LOG_LEGACYTIME_LEN) {
3406 int m;
3407
3408 /* supported header format according to rfc3164.
3409 * ex:
3410 * 'Jan 1 00:00:00 HOSTNAME TAG[PID]: '
3411 * or 'Jan 1 00:00:00 HOSTNAME TAG: '
3412 * or 'Jan 1 00:00:00 HOSTNAME '
3413 * Note: HOSTNAME is mandatory, and day
3414 * of month uses a single space prefix if
3415 * less than 10 to ensure hour offset is
3416 * always the same.
3417 */
3418
3419 /* Check month to see if it correspond to a rfc3164
3420 * header ex 'Jan 1 00:00:00' */
3421 for (m = 0; m < 12; m++)
3422 if (!memcmp(monthname[m], p, 3))
3423 break;
3424 /* Month not found */
3425 if (m == 12)
3426 goto bad_format;
3427
3428 metadata[LOG_META_TIME] = ist2(p, LOG_LEGACYTIME_LEN);
3429
3430 p += LOG_LEGACYTIME_LEN;
3431 if ((p - buf) >= buflen || *p != ' ')
3432 goto bad_format;
3433
3434 p++;
3435 if ((p - buf) >= buflen || *p == ' ')
3436 goto bad_format;
3437
3438 metadata[LOG_META_HOST].ptr = p;
3439 while (*p != ' ') {
3440 p++;
3441 if ((p - buf) >= buflen)
3442 goto bad_format;
3443 }
3444 metadata[LOG_META_HOST].len = p - metadata[LOG_META_HOST].ptr;
3445
3446 /* TAG seems to no be mandatory */
3447 p++;
3448
3449 buflen -= p - buf;
3450 buf = p;
3451
3452 *size = buflen;
3453 *message = buf;
3454
3455 if (!buflen)
3456 return;
3457
3458 while (((p - buf) < buflen) && *p != ' ' && *p != ':')
3459 p++;
3460
3461 /* a tag must present a trailing ':' */
3462 if (((p - buf) >= buflen) || *p != ':')
3463 return;
3464 p++;
3465 /* followed by a space */
3466 if (((p - buf) >= buflen) || *p != ' ')
3467 return;
3468
3469 /* rewind to parse tag and pid */
3470 p = buf;
3471 metadata[LOG_META_TAG].ptr = p;
3472 /* we have the guarantee that ':' will be reach before size limit */
3473 while (*p != ':') {
3474 if (*p == '[') {
3475 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3476 metadata[LOG_META_PID].ptr = p + 1;
3477 }
Tim Duesterhus7b5777d2021-03-02 18:57:28 +01003478 else if (*p == ']' && isttest(metadata[LOG_META_PID])) {
Emeric Brun54932b42020-07-07 09:43:24 +02003479 if (p[1] != ':')
3480 return;
3481 metadata[LOG_META_PID].len = p - metadata[LOG_META_PID].ptr;
3482 }
3483 p++;
3484 }
3485 if (!metadata[LOG_META_TAG].len)
3486 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3487
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05003488 /* let pass ':' and ' ', we still have warranty size is large enough */
Emeric Brun54932b42020-07-07 09:43:24 +02003489 p += 2;
3490
3491 buflen -= p - buf;
3492 buf = p;
3493
3494 *size = buflen;
3495 *message = buf;
3496 }
3497
3498 return;
3499
3500bad_format:
3501 /* bad syslog format, we reset all parsed syslog fields
3502 * but priority is kept because we are able to re-build
3503 * this message using LOF_FORMAT_PRIO.
3504 */
3505 metadata[LOG_META_TIME].len = 0;
3506 metadata[LOG_META_HOST].len = 0;
3507 metadata[LOG_META_TAG].len = 0;
3508 metadata[LOG_META_PID].len = 0;
3509 metadata[LOG_META_MSGID].len = 0;
3510 metadata[LOG_META_STDATA].len = 0;
3511
3512 return;
3513}
3514
3515/*
3516 * UDP syslog fd handler
3517 */
3518void syslog_fd_handler(int fd)
3519{
3520 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
3521 ssize_t ret = 0;
3522 struct buffer *buf = get_trash_chunk();
3523 size_t size;
3524 char *message;
3525 int level;
3526 int facility;
3527 struct listener *l = objt_listener(fdtab[fd].owner);
3528 int max_accept;
3529
3530 if(!l)
3531 ABORT_NOW();
3532
Willy Tarreauf5090652021-04-06 17:23:40 +02003533 if (fdtab[fd].state & FD_POLL_IN) {
Emeric Brun54932b42020-07-07 09:43:24 +02003534
3535 if (!fd_recv_ready(fd))
3536 return;
3537
3538 max_accept = l->maxaccept ? l->maxaccept : 1;
3539
3540 do {
3541 /* Source address */
3542 struct sockaddr_storage saddr = {0};
3543 socklen_t saddrlen;
3544
3545 saddrlen = sizeof(saddr);
3546
3547 ret = recvfrom(fd, buf->area, buf->size, 0, (struct sockaddr *)&saddr, &saddrlen);
3548 if (ret < 0) {
3549 if (errno == EINTR)
3550 continue;
3551 if (errno == EAGAIN)
3552 fd_cant_recv(fd);
3553 goto out;
3554 }
3555 buf->data = ret;
3556
Emeric Brun45c457a2020-07-09 23:23:34 +02003557 /* update counters */
Willy Tarreau4781b152021-04-06 13:53:36 +02003558 _HA_ATOMIC_INC(&cum_log_messages);
Emeric Bruna39ecbd2020-10-05 14:33:12 +02003559 proxy_inc_fe_req_ctr(l, l->bind_conf->frontend);
Emeric Brun45c457a2020-07-09 23:23:34 +02003560
Emeric Brun54932b42020-07-07 09:43:24 +02003561 parse_log_message(buf->area, buf->data, &level, &facility, metadata, &message, &size);
3562
3563 process_send_log(&l->bind_conf->frontend->logsrvs, level, facility, metadata, message, size);
3564
3565 } while (--max_accept);
3566 }
3567
3568out:
3569 return;
3570}
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003571
Emeric Brun12941c82020-07-07 14:19:42 +02003572/*
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003573 * IO Handler to handle message exchange with a syslog tcp client
3574 */
3575static void syslog_io_handler(struct appctx *appctx)
3576{
3577 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
3578 struct stream_interface *si = appctx->owner;
3579 struct stream *s = si_strm(si);
3580 struct proxy *frontend = strm_fe(s);
3581 struct listener *l = strm_li(s);
3582 struct buffer *buf = get_trash_chunk();
3583 int max_accept;
3584 int to_skip;
3585 int facility;
3586 int level;
3587 char *message;
3588 size_t size;
3589
3590 max_accept = l->maxaccept ? l->maxaccept : 1;
3591 while (co_data(si_oc(si))) {
3592 char c;
3593
3594 if (max_accept <= 0)
3595 goto missing_budget;
3596 max_accept--;
3597
3598 to_skip = co_getchar(si_oc(si), &c);
3599 if (!to_skip)
3600 goto missing_data;
3601 else if (to_skip < 0)
3602 goto cli_abort;
3603
3604 if (c == '<') {
3605 /* rfc-6587, Non-Transparent-Framing: messages separated by
3606 * a trailing LF or CR LF
3607 */
3608 to_skip = co_getline(si_oc(si), buf->area, buf->size);
3609 if (!to_skip)
3610 goto missing_data;
3611 else if (to_skip < 0)
3612 goto cli_abort;
3613
3614 if (buf->area[to_skip - 1] != '\n')
3615 goto parse_error;
3616
3617 buf->data = to_skip - 1;
3618
3619 /* according to rfc-6587, some devices adds CR before LF */
3620 if (buf->data && buf->area[buf->data - 1] == '\r')
3621 buf->data--;
3622
3623 }
3624 else if ((unsigned char)(c - '1') <= 8) {
3625 /* rfc-6587, Octet-Counting: message length in ASCII
3626 * (first digit can not be ZERO), followed by a space
3627 * and message length
3628 */
3629 char *p = NULL;
3630 int msglen;
3631
3632 to_skip = co_getword(si_oc(si), buf->area, buf->size, ' ');
3633 if (!to_skip)
3634 goto missing_data;
3635 else if (to_skip < 0)
3636 goto cli_abort;
3637
3638 if (buf->area[to_skip - 1] != ' ')
3639 goto parse_error;
3640
3641 msglen = strtol(trash.area, &p, 10);
3642 if (!msglen || p != &buf->area[to_skip - 1])
3643 goto parse_error;
3644
3645 /* message seems too large */
3646 if (msglen > buf->size)
3647 goto parse_error;
3648
3649 msglen = co_getblk(si_oc(si), buf->area, msglen, to_skip);
3650 if (!msglen)
3651 goto missing_data;
3652 else if (msglen < 0)
3653 goto cli_abort;
3654
3655
3656 buf->data = msglen;
3657 to_skip += msglen;
3658 }
3659 else
3660 goto parse_error;
3661
3662 co_skip(si_oc(si), to_skip);
3663
3664 /* update counters */
Willy Tarreau4781b152021-04-06 13:53:36 +02003665 _HA_ATOMIC_INC(&cum_log_messages);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003666 proxy_inc_fe_req_ctr(l, frontend);
3667
3668 parse_log_message(buf->area, buf->data, &level, &facility, metadata, &message, &size);
3669
3670 process_send_log(&frontend->logsrvs, level, facility, metadata, message, size);
3671
3672 }
3673
3674missing_data:
3675 /* we need more data to read */
3676 si_oc(si)->flags |= CF_READ_DONTWAIT;
3677
3678 return;
3679
3680missing_budget:
3681 /* it may remain some stuff to do, let's retry later */
3682 appctx_wakeup(appctx);
3683
3684 return;
3685
3686parse_error:
3687 if (l->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02003688 _HA_ATOMIC_INC(&l->counters->failed_req);
3689 _HA_ATOMIC_INC(&frontend->fe_counters.failed_req);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003690
3691 goto close;
3692
3693cli_abort:
3694 if (l->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02003695 _HA_ATOMIC_INC(&l->counters->cli_aborts);
3696 _HA_ATOMIC_INC(&frontend->fe_counters.cli_aborts);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003697
3698close:
3699 si_shutw(si);
3700 si_shutr(si);
3701
3702 si_ic(si)->flags |= CF_READ_NULL;
3703
3704 return;
3705}
3706
3707static struct applet syslog_applet = {
3708 .obj_type = OBJ_TYPE_APPLET,
3709 .name = "<SYSLOG>", /* used for logging */
3710 .fct = syslog_io_handler,
3711 .release = NULL,
3712};
3713
3714/*
Emeric Brun12941c82020-07-07 14:19:42 +02003715 * Parse "log-forward" section and create corresponding sink buffer.
3716 *
3717 * The function returns 0 in success case, otherwise, it returns error
3718 * flags.
3719 */
3720int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3721{
3722 int err_code = 0;
3723 struct proxy *px;
3724 char *errmsg = NULL;
3725 const char *err = NULL;
3726
3727 if (strcmp(args[0], "log-forward") == 0) {
3728 if (!*args[1]) {
3729 ha_alert("parsing [%s:%d] : missing name for ip-forward section.\n", file, linenum);
3730 err_code |= ERR_ALERT | ERR_ABORT;
3731 goto out;
3732 }
3733
3734 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3735 goto out;
3736
3737 err = invalid_char(args[1]);
3738 if (err) {
3739 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3740 file, linenum, *err, args[0], args[1]);
3741 err_code |= ERR_ALERT | ERR_ABORT;
3742 goto out;
3743 }
3744
Emeric Brunb0c331f2020-10-07 17:05:59 +02003745 px = log_forward_by_name(args[1]);
3746 if (px) {
3747 ha_alert("Parsing [%s:%d]: log-forward section '%s' has the same name as another log-forward section declared at %s:%d.\n",
3748 file, linenum, args[1], px->conf.file, px->conf.line);
3749 err_code |= ERR_ALERT | ERR_FATAL;
3750 }
3751
3752 px = proxy_find_by_name(args[1], 0, 0);
3753 if (px) {
3754 ha_alert("Parsing [%s:%d]: log forward section '%s' has the same name as %s '%s' declared at %s:%d.\n",
3755 file, linenum, args[1], proxy_type_str(px),
3756 px->id, px->conf.file, px->conf.line);
3757 err_code |= ERR_ALERT | ERR_FATAL;
Emeric Brun12941c82020-07-07 14:19:42 +02003758 }
3759
3760 px = calloc(1, sizeof *px);
3761 if (!px) {
3762 err_code |= ERR_ALERT | ERR_FATAL;
3763 goto out;
3764 }
3765
3766 px->next = cfg_log_forward;
3767 cfg_log_forward = px;
3768
3769 init_new_proxy(px);
3770 px->conf.file = strdup(file);
3771 px->conf.line = linenum;
3772 px->mode = PR_MODE_SYSLOG;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003773 px->last_change = now.tv_sec;
3774 px->cap = PR_CAP_FE;
3775 px->maxconn = 10;
3776 px->timeout.client = TICK_ETERNITY;
3777 px->accept = frontend_accept;
3778 px->default_target = &syslog_applet.obj_type;
Emeric Brun12941c82020-07-07 14:19:42 +02003779 px->id = strdup(args[1]);
3780
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003781 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003782 else if (strcmp(args[0], "maxconn") == 0) { /* maxconn */
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003783 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], " Maybe you want 'fullconn' instead ?"))
3784 err_code |= ERR_WARN;
3785
3786 if (*(args[1]) == 0) {
3787 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
3788 err_code |= ERR_ALERT | ERR_FATAL;
3789 goto out;
3790 }
3791 cfg_log_forward->maxconn = atol(args[1]);
3792 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3793 goto out;
3794 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003795 else if (strcmp(args[0], "backlog") == 0) { /* backlog */
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003796 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], NULL))
3797 err_code |= ERR_WARN;
3798
3799 if (*(args[1]) == 0) {
3800 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
3801 err_code |= ERR_ALERT | ERR_FATAL;
3802 goto out;
3803 }
3804 cfg_log_forward->backlog = atol(args[1]);
3805 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3806 goto out;
3807 }
3808 else if (strcmp(args[0], "bind") == 0) {
3809 int cur_arg;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003810 struct bind_conf *bind_conf;
3811 struct bind_kw *kw;
3812 struct listener *l;
3813
3814 cur_arg = 1;
3815
3816 bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum,
3817 NULL, xprt_get(XPRT_RAW));
3818 if (!bind_conf) {
3819 ha_alert("parsing [%s:%d] : out of memory error.", file, linenum);
3820 err_code |= ERR_ALERT | ERR_FATAL;
3821 goto out;
3822 }
3823
3824 if (!str2listener(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) {
3825 if (errmsg && *errmsg) {
3826 indent_msg(&errmsg, 2);
3827 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3828 }
3829 else {
3830 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3831 file, linenum, args[0], args[1], args[2]);
3832 err_code |= ERR_ALERT | ERR_FATAL;
3833 goto out;
3834 }
3835 }
3836 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
Willy Tarreau66161322021-02-19 15:50:27 +01003837 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003838 l->accept = session_accept_fd;
3839 l->analysers |= cfg_log_forward->fe_req_ana;
3840 l->default_target = cfg_log_forward->default_target;
3841 global.maxsock++;
3842 }
3843 cur_arg++;
3844
3845 while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) {
3846 int ret;
3847
3848 ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg);
3849 err_code |= ret;
3850 if (ret) {
3851 if (errmsg && *errmsg) {
3852 indent_msg(&errmsg, 2);
3853 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
3854 }
3855 else
3856 ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n",
3857 file, linenum, args[cur_arg]);
3858 if (ret & ERR_FATAL)
3859 goto out;
3860 }
3861 cur_arg += 1 + kw->skip;
3862 }
3863 if (*args[cur_arg] != 0) {
Willy Tarreau433b05f2021-03-12 10:14:07 +01003864 const char *best = bind_find_best_kw(args[cur_arg]);
3865 if (best)
3866 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
3867 file, linenum, args[cur_arg], cursection, best);
3868 else
3869 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
3870 file, linenum, args[cur_arg], cursection);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003871 err_code |= ERR_ALERT | ERR_FATAL;
3872 goto out;
3873 }
Emeric Brun12941c82020-07-07 14:19:42 +02003874 }
Willy Tarreau76aaa7f2020-09-16 15:07:22 +02003875 else if (strcmp(args[0], "dgram-bind") == 0) {
Emeric Brun12941c82020-07-07 14:19:42 +02003876 int cur_arg;
Emeric Brun12941c82020-07-07 14:19:42 +02003877 struct bind_conf *bind_conf;
3878 struct bind_kw *kw;
3879 struct listener *l;
3880
3881 cur_arg = 1;
3882
3883 bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum,
3884 NULL, xprt_get(XPRT_RAW));
Christopher Faulet0c6d1dc2021-04-12 16:56:37 +02003885 if (!bind_conf) {
3886 ha_alert("parsing [%s:%d] : out of memory error.", file, linenum);
3887 err_code |= ERR_ALERT | ERR_FATAL;
3888 goto out;
3889 }
Emeric Brun12941c82020-07-07 14:19:42 +02003890
Willy Tarreau26ff5da2020-09-16 15:22:19 +02003891 if (!str2receiver(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) {
Emeric Brun12941c82020-07-07 14:19:42 +02003892 if (errmsg && *errmsg) {
3893 indent_msg(&errmsg, 2);
3894 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3895 }
3896 else {
3897 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3898 file, linenum, args[0], args[1], args[2]);
Emeric Brun12941c82020-07-07 14:19:42 +02003899 }
Willy Tarreau3b139e52020-09-16 16:24:14 +02003900 err_code |= ERR_ALERT | ERR_FATAL;
3901 goto out;
Emeric Brun12941c82020-07-07 14:19:42 +02003902 }
3903 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
Willy Tarreau26ff5da2020-09-16 15:22:19 +02003904 /* the fact that the sockets are of type dgram is guaranteed by str2receiver() */
Willy Tarreau66161322021-02-19 15:50:27 +01003905 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
Willy Tarreaue140a692020-10-15 21:25:32 +02003906 l->rx.iocb = syslog_fd_handler;
Emeric Brun12941c82020-07-07 14:19:42 +02003907 global.maxsock++;
3908 }
3909 cur_arg++;
3910
3911 while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) {
3912 int ret;
3913
3914 ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg);
3915 err_code |= ret;
3916 if (ret) {
3917 if (errmsg && *errmsg) {
3918 indent_msg(&errmsg, 2);
3919 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
3920 }
3921 else
3922 ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n",
3923 file, linenum, args[cur_arg]);
3924 if (ret & ERR_FATAL)
3925 goto out;
3926 }
3927 cur_arg += 1 + kw->skip;
3928 }
3929 if (*args[cur_arg] != 0) {
Willy Tarreau433b05f2021-03-12 10:14:07 +01003930 const char *best = bind_find_best_kw(args[cur_arg]);
3931 if (best)
3932 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
3933 file, linenum, args[cur_arg], cursection, best);
3934 else
3935 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
3936 file, linenum, args[cur_arg], cursection);
Emeric Brun12941c82020-07-07 14:19:42 +02003937 err_code |= ERR_ALERT | ERR_FATAL;
3938 goto out;
3939 }
3940 }
3941 else if (strcmp(args[0], "log") == 0) {
Emeric Brun9533a702021-04-02 10:13:43 +02003942 if (!parse_logsrv(args, &cfg_log_forward->logsrvs, (kwm == KWM_NO), file, linenum, &errmsg)) {
Emeric Brun12941c82020-07-07 14:19:42 +02003943 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3944 err_code |= ERR_ALERT | ERR_FATAL;
3945 goto out;
3946 }
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003947 }
3948 else if (strcmp(args[0], "timeout") == 0) {
3949 const char *res;
3950 unsigned timeout;
3951
3952 if (strcmp(args[1], "client") != 0) {
3953 ha_alert("parsing [%s:%d] : unknown keyword '%s %s' in log-forward section.\n", file, linenum, args[0], args[1]);
3954 err_code |= ERR_ALERT | ERR_FATAL;
3955 goto out;
3956 }
3957
3958 if (*args[2] == 0) {
3959 ha_alert("parsing [%s:%d] : missing timeout client value.\n", file, linenum);
3960 err_code |= ERR_ALERT | ERR_FATAL;
3961 goto out;
3962 }
3963 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3964 if (res == PARSE_TIME_OVER) {
3965 memprintf(&errmsg, "timer overflow in argument '%s' to 'timeout client' (maximum value is 2147483647 ms or ~24.8 days)", args[2]);
3966 }
3967 else if (res == PARSE_TIME_UNDER) {
3968 memprintf(&errmsg, "timer underflow in argument '%s' to 'timeout client' (minimum non-null value is 1 ms)", args[2]);
3969 }
3970 else if (res) {
3971 memprintf(&errmsg, "unexpected character '%c' in 'timeout client'", *res);
3972 return -1;
3973 }
3974
3975 if (res) {
3976 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3977 err_code |= ERR_ALERT | ERR_FATAL;
3978 goto out;
3979 }
3980 cfg_log_forward->timeout.client = MS_TO_TICKS(timeout);
Emeric Brun12941c82020-07-07 14:19:42 +02003981 }
Willy Tarreauf9feec22020-09-16 15:04:33 +02003982 else {
3983 ha_alert("parsing [%s:%d] : unknown keyword '%s' in log-forward section.\n", file, linenum, args[0]);
3984 err_code |= ERR_ALERT | ERR_ABORT;
3985 goto out;
3986 }
Emeric Brun12941c82020-07-07 14:19:42 +02003987out:
3988 return err_code;
3989}
3990
Willy Tarreau0108d902018-11-25 19:14:37 +01003991
Emeric Brun12941c82020-07-07 14:19:42 +02003992/* config parsers for this section */
3993REGISTER_CONFIG_SECTION("log-forward", cfg_parse_log_forward, NULL);
3994
Willy Tarreau082b6282019-05-22 14:42:12 +02003995REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3996REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003997
Willy Tarreaubaaee002006-06-26 02:48:02 +02003998/*
3999 * Local variables:
4000 * c-indent-level: 8
4001 * c-basic-offset: 8
4002 * End:
4003 */