blob: cbb86e57b931f43d6270f52633e4e0daf82d6bba [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 Tarreau55542642021-10-08 09:33:24 +020030#include <haproxy/clock.h>
Christopher Faulet908628c2022-03-25 16:43:49 +010031#include <haproxy/conn_stream.h>
32#include <haproxy/cs_utils.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020033#include <haproxy/fd.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020034#include <haproxy/frontend.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020035#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020036#include <haproxy/http.h>
Willy Tarreaub7fc4c42021-10-06 18:56:42 +020037#include <haproxy/http_ana.h>
Emeric Brun12941c82020-07-07 14:19:42 +020038#include <haproxy/listener.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020039#include <haproxy/log.h>
Emeric Brun12941c82020-07-07 14:19:42 +020040#include <haproxy/proxy.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020041#include <haproxy/sample.h>
42#include <haproxy/sink.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020043#include <haproxy/ssl_sock.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020044#include <haproxy/stream.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020045#include <haproxy/stream_interface.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020046#include <haproxy/time.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020047#include <haproxy/tools.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020048
Emeric Brun45c457a2020-07-09 23:23:34 +020049/* global recv logs counter */
50int cum_log_messages;
Willy Tarreaubaaee002006-06-26 02:48:02 +020051
Emeric Brun12941c82020-07-07 14:19:42 +020052/* log forward proxy list */
53struct proxy *cfg_log_forward;
54
Emeric Brun54648852020-07-06 15:54:06 +020055struct log_fmt_st {
Dragan Dosen43885c72015-10-01 13:18:13 +020056 char *name;
Dragan Dosen43885c72015-10-01 13:18:13 +020057};
58
Emeric Brun54648852020-07-06 15:54:06 +020059static const struct log_fmt_st log_formats[LOG_FORMATS] = {
Emeric Brun0237c4e2020-11-27 16:24:34 +010060 [LOG_FORMAT_LOCAL] = {
61 .name = "local",
62 },
Dragan Dosen43885c72015-10-01 13:18:13 +020063 [LOG_FORMAT_RFC3164] = {
64 .name = "rfc3164",
Dragan Dosen43885c72015-10-01 13:18:13 +020065 },
66 [LOG_FORMAT_RFC5424] = {
67 .name = "rfc5424",
Emeric Brun54648852020-07-06 15:54:06 +020068 },
69 [LOG_FORMAT_PRIO] = {
70 .name = "priority",
Willy Tarreaue8746a02018-11-12 08:45:00 +010071 },
72 [LOG_FORMAT_SHORT] = {
73 .name = "short",
Emeric Brun54648852020-07-06 15:54:06 +020074 },
75 [LOG_FORMAT_TIMED] = {
76 .name = "timed",
77 },
78 [LOG_FORMAT_ISO] = {
79 .name = "iso",
Willy Tarreaue8746a02018-11-12 08:45:00 +010080 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010081 [LOG_FORMAT_RAW] = {
82 .name = "raw",
Willy Tarreauc1b06452018-11-12 11:57:56 +010083 },
Dragan Dosen1322d092015-09-22 16:05:32 +020084};
85
Dragan Dosen835b9212016-02-12 13:23:03 +010086/*
87 * This map is used with all the FD_* macros to check whether a particular bit
Willy Tarreau1bfd6022019-06-07 11:10:07 +020088 * is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
89 * bytes which should be escaped. When ha_bit_test() returns non-zero, it means
90 * that the byte should be escaped. Be careful to always pass bytes from 0 to
91 * 255 exclusively to the macros.
Dragan Dosen835b9212016-02-12 13:23:03 +010092 */
Willy Tarreau1bfd6022019-06-07 11:10:07 +020093long rfc5424_escape_map[(256/8) / sizeof(long)];
94long hdr_encode_map[(256/8) / sizeof(long)];
95long url_encode_map[(256/8) / sizeof(long)];
96long http_encode_map[(256/8) / sizeof(long)];
Dragan Dosen835b9212016-02-12 13:23:03 +010097
Dragan Dosen835b9212016-02-12 13:23:03 +010098
Willy Tarreaubaaee002006-06-26 02:48:02 +020099const char *log_facilities[NB_LOG_FACILITIES] = {
100 "kern", "user", "mail", "daemon",
101 "auth", "syslog", "lpr", "news",
102 "uucp", "cron", "auth2", "ftp",
103 "ntp", "audit", "alert", "cron2",
104 "local0", "local1", "local2", "local3",
105 "local4", "local5", "local6", "local7"
106};
107
Willy Tarreaubaaee002006-06-26 02:48:02 +0200108const char *log_levels[NB_LOG_LEVELS] = {
109 "emerg", "alert", "crit", "err",
110 "warning", "notice", "info", "debug"
111};
112
Willy Tarreau570f2212013-06-10 16:42:09 +0200113const 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 +0200114const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200115
William Lallemand723b73a2012-02-08 16:37:49 +0100116
117/* log_format */
118struct logformat_type {
119 char *name;
120 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100121 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200122 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100123 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
William Lallemand723b73a2012-02-08 16:37:49 +0100124};
125
William Lallemandb7ff6a32012-03-02 14:35:21 +0100126int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
127
William Lallemand723b73a2012-02-08 16:37:49 +0100128/* log_format variable names */
129static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200130 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100131
132 /* please keep these lines sorted ! */
133 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
134 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
135 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
136 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
Tim Duesterhuscf6e0c82020-03-13 12:34:24 +0100137 { "ID", LOG_FMT_UNIQUEID, PR_MODE_TCP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200138 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200139 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200140 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100141 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200142 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
143 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
144 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
145 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
146 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
147 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200148 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100149 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200150 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Damien Claisse57c8eb92020-04-28 12:09:19 +0000151 { "Tu", LOG_FMT_TU, PR_MODE_TCP, LW_BYTES, NULL }, /* Tu = Tt -Ti */
Willy Tarreau2beef582012-12-20 17:22:52 +0100152 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
153 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200154 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100155 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200156 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100157 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
158 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200159 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200160 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
161 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100162 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
163 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200164 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
165 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100166 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200167 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
168 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
169 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
170 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000171 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
Maciej Zdebfcdfd852020-11-30 18:27:47 +0000172 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP relative or absolute path */
173 { "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 +0000174 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000175 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
176 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200177 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100178 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200179 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100180 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
181 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100182 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100183 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
184 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
185 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
186 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
187 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200188 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
189 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100190 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200191 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
192 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
193 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100194 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
195 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
William Lallemand5e19a282012-04-02 16:22:10 +0200196 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100197};
198
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200199char 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
Willy Tarreau68574dd2021-11-05 19:14:55 +0100200char default_https_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 %[fc_err]/%[ssl_fc_err,hex]/%[ssl_c_err]/%[ssl_c_ca_err]/%[ssl_fc_is_resumed] %[ssl_fc_sni]/%sslv/%sslc";
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200201char 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 +0100202char 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 +0100203char *log_format = NULL;
204
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200205/* Default string used for structured-data part in RFC5424 formatted
206 * syslog messages.
207 */
208char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200209
Willy Tarreau13ef7732018-11-12 07:25:28 +0100210/* total number of dropped logs */
211unsigned int dropped_logs = 0;
212
Dragan Dosen59cee972015-09-19 22:09:02 +0200213/* This is a global syslog message buffer, common to all outgoing
214 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100215 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200216THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100217
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200218/* A global syslog message buffer, common to all RFC5424 syslog messages.
219 * Currently, it is used for generating the structured-data part.
220 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200221THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200222
William Lallemand723b73a2012-02-08 16:37:49 +0100223struct logformat_var_args {
224 char *name;
225 int mask;
226};
227
228struct logformat_var_args var_args_list[] = {
229// global
230 { "M", LOG_OPT_MANDATORY },
231 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200232 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100233 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100234 { 0, 0 }
235};
236
237/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100238 * callback used to configure addr source retrieval
239 */
240int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
241{
242 curproxy->options2 |= PR_O2_SRC_ADDR;
243
244 return 0;
245}
246
247
248/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100249 * Parse args in a logformat_var. Returns 0 in error
250 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100251 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100252int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100253{
254 int i = 0;
255 int end = 0;
256 int flags = 0; // 1 = + 2 = -
257 char *sp = NULL; // start pointer
258
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100259 if (args == NULL) {
260 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100261 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100262 }
William Lallemand723b73a2012-02-08 16:37:49 +0100263
264 while (1) {
265 if (*args == '\0')
266 end = 1;
267
268 if (*args == '+') {
269 // add flag
270 sp = args + 1;
271 flags = 1;
272 }
273 if (*args == '-') {
274 // delete flag
275 sp = args + 1;
276 flags = 2;
277 }
278
279 if (*args == '\0' || *args == ',') {
280 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100281 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100282 if (strcmp(sp, var_args_list[i].name) == 0) {
283 if (flags == 1) {
284 node->options |= var_args_list[i].mask;
285 break;
286 } else if (flags == 2) {
287 node->options &= ~var_args_list[i].mask;
288 break;
289 }
290 }
291 }
292 sp = NULL;
293 if (end)
294 break;
295 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100296 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100297 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100298 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100299}
300
301/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100302 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
303 * must pass the args part in the <arg> pointer with its length in <arg_len>,
304 * and varname with its length in <var> and <var_len> respectively. <arg> is
305 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100306 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100307 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100308int 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 +0100309{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100310 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200311 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100312
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100313 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
314 if (strlen(logformat_keywords[j].name) == var_len &&
315 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
316 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200317 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100318 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100319 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200320 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100321 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100322 node->type = logformat_keywords[j].type;
323 node->options = *defoptions;
324 if (arg_len) {
325 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100326 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200327 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100328 }
329 if (node->type == LOG_FMT_GLOBAL) {
330 *defoptions = node->options;
331 free(node->arg);
332 free(node);
333 } else {
334 if (logformat_keywords[j].config_callback &&
335 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200336 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100337 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100338 curproxy->to_log |= logformat_keywords[j].lw;
Willy Tarreau2b718102021-04-21 07:32:39 +0200339 LIST_APPEND(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100340 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100341 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100342 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100343 memprintf(err, "format variable '%s' is reserved for HTTP mode",
344 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200345 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100346 }
William Lallemand723b73a2012-02-08 16:37:49 +0100347 }
348 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100349
350 j = var[var_len];
351 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100352 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 +0100353 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200354
355 error_free:
356 if (node) {
357 free(node->arg);
358 free(node);
359 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100360 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100361}
362
363/*
364 * push to the logformat linked list
365 *
366 * start: start pointer
367 * end: end text pointer
368 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100369 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100370 *
371 * LOG_TEXT: copy chars from start to end excluding end.
372 *
373*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100374int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100375{
376 char *str;
377
Willy Tarreaua3571662012-12-20 21:59:12 +0100378 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200379 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100380 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100381 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100382 return 0;
383 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200384 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100385 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100386 str[end - start] = '\0';
387 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100388 node->type = LOG_FMT_TEXT; // type string
Willy Tarreau2b718102021-04-21 07:32:39 +0200389 LIST_APPEND(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100390 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200391 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100392 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100393 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100394 return 0;
395 }
William Lallemand1d705562012-03-12 12:46:41 +0100396 node->type = LOG_FMT_SEPARATOR;
Willy Tarreau2b718102021-04-21 07:32:39 +0200397 LIST_APPEND(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100398 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100399 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100400}
401
402/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100403 * Parse the sample fetch expression <text> and add a node to <list_format> upon
404 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100405 * should work. The curpx->conf.args.ctx must be set by the caller. If an end pointer
406 * is passed in <endptr>, it will be updated with the pointer to the first character
407 * not part of the sample expression.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100408 *
409 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100410 */
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100411int 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 +0100412{
413 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200414 struct sample_expr *expr = NULL;
415 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100416 int cmd_arg;
417
418 cmd[0] = text;
419 cmd[1] = "";
420 cmd_arg = 0;
421
Christopher Fauleteaba25d2021-09-30 16:22:51 +0200422 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err,
Christopher Faulet6ff7de52021-10-13 15:18:36 +0200423 &curpx->conf.args, endptr);
Willy Tarreauc8368452012-12-21 00:09:23 +0100424 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100425 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200426 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100427 }
428
Vincent Bernat02779b62016-04-03 13:48:43 +0200429 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100430 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100431 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200432 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100433 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100434 node->type = LOG_FMT_EXPR;
435 node->expr = expr;
436 node->options = options;
437
438 if (arg_len) {
439 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100440 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200441 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100442 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100443 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100444 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
445
Willy Tarreau434c57c2013-01-08 01:10:24 +0100446 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100447 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
448
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100449 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100450 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
451 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200452 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100453 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100454
Christopher Faulet581db2b2021-03-26 10:02:46 +0100455 if ((options & LOG_OPT_HTTP) && (expr->fetch->use & (SMP_USE_L6REQ|SMP_USE_L6RES))) {
456 ha_warning("parsing [%s:%d] : L6 sample fetch <%s> ignored in HTTP log-format string.\n",
457 curpx->conf.args.file, curpx->conf.args.line, text);
458 }
459
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200460 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreauc8368452012-12-21 00:09:23 +0100461 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100462 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100463
William Lallemand65ad6e12014-01-31 15:08:02 +0100464 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
465 * needed with some sample fetches (eg: ssl*). We always set it for
466 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100467 */
468 curpx->to_log |= LW_XPRT;
Christopher Fauletd2236cd2020-04-06 18:29:14 +0200469 if (curpx->http_needed)
470 curpx->to_log |= LW_REQ;
Willy Tarreau2b718102021-04-21 07:32:39 +0200471 LIST_APPEND(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100472 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200473
474 error_free:
475 release_sample_expr(expr);
476 if (node) {
477 free(node->arg);
478 free(node);
479 }
480 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100481}
482
483/*
William Lallemand723b73a2012-02-08 16:37:49 +0100484 * Parse the log_format string and fill a linked list.
485 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200486 * You can set arguments using { } : %{many arguments}varname.
487 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100488 *
Ilya Shipitsinae40dbc2020-04-04 12:59:53 +0500489 * fmt: the string to parse
William Lallemand1d705562012-03-12 12:46:41 +0100490 * curproxy: the proxy affected
491 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100492 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100493 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100494 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100495 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100496 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100497int 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 +0100498{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100499 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100500 char *arg = NULL; /* start pointer for args */
501 char *var = NULL; /* start pointer for vars */
502 int arg_len = 0;
503 int var_len = 0;
504 int cformat; /* current token format */
505 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100506 struct logformat_node *tmplf, *back;
507
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100508 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100509 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100510 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100511 return 0;
512 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200513 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200514
William Lallemand723b73a2012-02-08 16:37:49 +0100515 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100516 list_for_each_entry_safe(tmplf, back, list_format, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200517 LIST_DELETE(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200518 release_sample_expr(tmplf->expr);
519 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100520 free(tmplf);
521 }
522
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100523 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100524 pformat = cformat;
525
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100526 if (!*str)
527 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100528
Joseph Herlant85b40592018-11-15 12:10:04 -0800529 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100530 * second have all common paths processed at one place. The common paths are the ones
531 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
532 * We use the common LF_INIT state to dispatch to the different final states.
533 */
534 switch (pformat) {
535 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100536 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100537 arg_len = var_len = 0;
538 if (*str == '{') { // optional argument
539 cformat = LF_STARG;
540 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100541 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100542 else if (*str == '[') {
543 cformat = LF_STEXPR;
544 var = str + 1; // store expr in variable name
545 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100546 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100547 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100548 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100549 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100550 else if (*str == '%')
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500551 cformat = LF_TEXT; // convert this character to a literal (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100552 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100553 /* single '%' followed by blank or digit, send them both */
554 cformat = LF_TEXT;
555 pformat = LF_TEXT; /* finally we include the previous char as well */
556 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600557 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 +0100558 *str, (int)(str - backfmt), fmt);
Willy Tarreau51013e82019-12-11 12:05:39 +0100559 goto fail;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100560
561 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100562 else
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500563 cformat = LF_INIT; // handle other cases of literals
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100564 break;
565
566 case LF_STARG: // text immediately following '%{'
567 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100568 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100569 arg_len = str - arg;
570 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100571 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100572 break;
573
574 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100575 if (*str == '[') {
576 cformat = LF_STEXPR;
577 var = str + 1; // store expr in variable name
578 break;
579 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100580 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100581 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100582 var = str;
583 break;
584 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100585 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Willy Tarreau51013e82019-12-11 12:05:39 +0100586 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100587
Willy Tarreauc8368452012-12-21 00:09:23 +0100588 case LF_STEXPR: // text immediately following '%['
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100589 /* the whole sample expression is parsed at once,
590 * returning the pointer to the first character not
591 * part of the expression, which MUST be the trailing
592 * angle bracket.
593 */
594 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &str))
595 goto fail;
596
597 if (*str == ']') {
598 // end of arg, go on with next state
599 cformat = pformat = LF_EDEXPR;
600 sp = str;
601 }
602 else {
603 char c = *str;
604 *str = 0;
Willy Tarreau90807112020-02-25 08:16:33 +0100605 if (isprint((unsigned char)c))
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100606 memprintf(err, "expected ']' after '%s', but found '%c'", var, c);
607 else
608 memprintf(err, "missing ']' after '%s'", var);
Dragan Dosen2866acf2020-06-30 21:16:43 +0200609 goto fail;
Willy Tarreauc8368452012-12-21 00:09:23 +0100610 }
611 break;
612
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100613 case LF_VAR: // text part of a variable name
614 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100615 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100616 cformat = LF_INIT; // not variable name anymore
617 break;
618
Willy Tarreauc8368452012-12-21 00:09:23 +0100619 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100620 cformat = LF_INIT;
621 }
622
623 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
624 switch (*str) {
625 case '%': cformat = LF_STARTVAR; break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100626 case 0 : cformat = LF_END; break;
Dragan Dosen1e3b16f2020-06-23 18:16:44 +0200627 case ' ':
628 if (options & LOG_OPT_MERGE_SPACES) {
629 cformat = LF_SEPARATOR;
630 break;
631 }
632 /* fall through */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100633 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100634 }
635 }
636
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100637 if (cformat != pformat || pformat == LF_SEPARATOR) {
638 switch (pformat) {
639 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100640 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100641 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100642 break;
643 case LF_TEXT:
644 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100645 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100646 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100647 break;
648 }
649 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100650 }
651 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100652
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100653 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100654 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Willy Tarreau51013e82019-12-11 12:05:39 +0100655 goto fail;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100656 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100657 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100658
659 return 1;
Willy Tarreau51013e82019-12-11 12:05:39 +0100660 fail:
661 free(backfmt);
662 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100663}
664
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200665/*
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500666 * 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 +0200667 * ranges of indexes. Note that an index may be considered as a particular range
668 * with a high limit to the low limit.
669 */
670int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
671{
672 char *end, *p;
673
674 *low = *high = 0;
675
676 p = *arg;
677 end = strchr(p, ',');
678 if (!end)
679 end = p + strlen(p);
680
681 *high = *low = read_uint((const char **)&p, end);
682 if (!*low || (p != end && *p != '-'))
683 goto err;
684
685 if (p == end)
686 goto done;
687
688 p++;
689 *high = read_uint((const char **)&p, end);
690 if (!*high || *high <= *low || p != end)
691 goto err;
692
693 done:
694 if (*end == ',')
695 end++;
696 *arg = end;
697 return 1;
698
699 err:
700 memprintf(err, "wrong sample range '%s'", *arg);
701 return 0;
702}
703
704/*
705 * Returns 1 if the range defined by <low> and <high> overlaps
706 * one of them in <rgs> array of ranges with <sz> the size of this
707 * array, 0 if not.
708 */
709int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
710 unsigned int low, unsigned int high, char **err)
711{
712 size_t i;
713
714 for (i = 0; i < sz; i++) {
715 if ((low >= rgs[i].low && low <= rgs[i].high) ||
716 (high >= rgs[i].low && high <= rgs[i].high)) {
717 memprintf(err, "ranges are overlapping");
718 return 1;
719 }
720 }
721
722 return 0;
723}
724
725int smp_log_range_cmp(const void *a, const void *b)
726{
727 const struct smp_log_range *rg_a = a;
728 const struct smp_log_range *rg_b = b;
729
730 if (rg_a->high < rg_b->low)
731 return -1;
732 else if (rg_a->low > rg_b->high)
733 return 1;
734
735 return 0;
736}
737
Willy Tarreau211ea252022-03-17 19:47:33 +0100738/* frees log server <logsrv> after freeing all of its allocated fields. The
739 * server must not belong to a list anymore. Logsrv may be NULL, which is
740 * silently ignored.
741 */
742void free_logsrv(struct logsrv *logsrv)
743{
744 if (!logsrv)
745 return;
746
747 BUG_ON(LIST_INLIST(&logsrv->list));
748 ha_free(&logsrv->conf.file);
749 ha_free(&logsrv->ring_name);
750 free(logsrv);
751}
752
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200753/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200754 * Parse "log" keyword and update <logsrvs> list accordingly.
755 *
756 * When <do_del> is set, it means the "no log" line was parsed, so all log
757 * servers in <logsrvs> are released.
758 *
759 * Otherwise, we try to parse the "log" line. First of all, when the list is not
760 * the global one, we look for the parameter "global". If we find it,
761 * global.logsrvs is copied. Else we parse each arguments.
762 *
763 * The function returns 1 in success case, otherwise, it returns 0 and err is
764 * filled.
765 */
Emeric Brun9533a702021-04-02 10:13:43 +0200766int parse_logsrv(char **args, struct list *logsrvs, int do_del, const char *file, int linenum, char **err)
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200767{
Willy Tarreauae32ac72020-10-27 09:51:37 +0100768 struct smp_log_range *smp_rgs = NULL;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200769 struct sockaddr_storage *sk;
Emeric Brun94aab062021-04-02 10:41:36 +0200770 struct protocol *proto;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200771 struct logsrv *logsrv = NULL;
772 int port1, port2;
773 int cur_arg;
Willy Tarreau89599262020-09-15 14:03:26 +0200774 int fd;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200775
776 /*
777 * "no log": delete previous herited or defined syslog
778 * servers.
779 */
780 if (do_del) {
781 struct logsrv *back;
782
783 if (*(args[1]) != 0) {
784 memprintf(err, "'no log' does not expect arguments");
785 goto error;
786 }
787
788 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
Willy Tarreau211ea252022-03-17 19:47:33 +0100789 LIST_DEL_INIT(&logsrv->list);
790 free_logsrv(logsrv);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200791 }
792 return 1;
793 }
794
795 /*
796 * "log global": copy global.logrsvs linked list to the end of logsrvs
797 * list. But first, we check (logsrvs != global.logsrvs).
798 */
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100799 if (*(args[1]) && *(args[2]) == 0 && strcmp(args[1], "global") == 0) {
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200800 if (logsrvs == &global.logsrvs) {
801 memprintf(err, "'global' is not supported for a global syslog server");
802 goto error;
803 }
804 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200805 struct logsrv *node;
806
807 list_for_each_entry(node, logsrvs, list) {
808 if (node->ref == logsrv)
809 goto skip_logsrv;
810 }
811
812 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200813 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200814 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200815 LIST_INIT(&node->list);
Willy Tarreau2b718102021-04-21 07:32:39 +0200816 LIST_APPEND(logsrvs, &node->list);
Willy Tarreau211ea252022-03-17 19:47:33 +0100817 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
Emeric Brun9533a702021-04-02 10:13:43 +0200818 node->conf.file = strdup(file);
819 node->conf.line = linenum;
Christopher Faulet28ac0992018-03-26 16:09:19 +0200820
821 skip_logsrv:
822 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200823 }
824 return 1;
825 }
826
827 /*
828 * "log <address> ...: parse a syslog server line
829 */
830 if (*(args[1]) == 0 || *(args[2]) == 0) {
831 memprintf(err, "expects <address> and <facility> %s as arguments",
832 ((logsrvs == &global.logsrvs) ? "" : "or global"));
833 goto error;
834 }
835
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100836 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
837 if (strcmp(args[1], "stdout") == 0)
838 args[1] = "fd@1";
839 else if (strcmp(args[1], "stderr") == 0)
840 args[1] = "fd@2";
841
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200842 logsrv = calloc(1, sizeof(*logsrv));
843 if (!logsrv) {
844 memprintf(err, "out of memory");
845 goto error;
846 }
Christopher Fauletb4f96ed2022-03-29 14:17:09 +0200847 LIST_INIT(&logsrv->list);
Emeric Brun9533a702021-04-02 10:13:43 +0200848 logsrv->conf.file = strdup(file);
849 logsrv->conf.line = linenum;
850
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200851 /* skip address for now, it will be parsed at the end */
852 cur_arg = 2;
853
854 /* just after the address, a length may be specified */
855 logsrv->maxlen = MAX_SYSLOG_LEN;
856 if (strcmp(args[cur_arg], "len") == 0) {
857 int len = atoi(args[cur_arg+1]);
858 if (len < 80 || len > 65535) {
859 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
860 args[cur_arg+1]);
861 goto error;
862 }
863 logsrv->maxlen = len;
864 cur_arg += 2;
865 }
866 if (logsrv->maxlen > global.max_syslog_len)
867 global.max_syslog_len = logsrv->maxlen;
868
869 /* after the length, a format may be specified */
870 if (strcmp(args[cur_arg], "format") == 0) {
871 logsrv->format = get_log_format(args[cur_arg+1]);
Emeric Brun54648852020-07-06 15:54:06 +0200872 if (logsrv->format == LOG_FORMAT_UNSPEC) {
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200873 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
874 goto error;
875 }
876 cur_arg += 2;
877 }
878
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200879 if (strcmp(args[cur_arg], "sample") == 0) {
880 unsigned low, high;
881 char *p, *beg, *end, *smp_sz_str;
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200882 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
883
884 p = args[cur_arg+1];
885 smp_sz_str = strchr(p, ':');
886 if (!smp_sz_str) {
887 memprintf(err, "Missing sample size");
888 goto error;
889 }
890
891 *smp_sz_str++ = '\0';
892
893 end = p + strlen(p);
894
895 while (p != end) {
896 if (!get_logsrv_smp_range(&low, &high, &p, err))
897 goto error;
898
899 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
900 goto error;
901
902 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
903 if (!smp_rgs) {
904 memprintf(err, "out of memory error");
905 goto error;
906 }
907
908 smp_rgs[smp_rgs_sz].low = low;
909 smp_rgs[smp_rgs_sz].high = high;
910 smp_rgs[smp_rgs_sz].sz = high - low + 1;
911 smp_rgs[smp_rgs_sz].curr_idx = 0;
912 if (smp_rgs[smp_rgs_sz].high > smp_sz)
913 smp_sz = smp_rgs[smp_rgs_sz].high;
914 smp_rgs_sz++;
915 }
916
Tim Duesterhus21648002019-06-23 22:10:10 +0200917 if (smp_rgs == NULL) {
918 memprintf(err, "no sampling ranges given");
919 goto error;
920 }
921
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200922 beg = smp_sz_str;
923 end = beg + strlen(beg);
924 new_smp_sz = read_uint((const char **)&beg, end);
925 if (!new_smp_sz || beg != end) {
926 memprintf(err, "wrong sample size '%s' for sample range '%s'",
927 smp_sz_str, args[cur_arg+1]);
928 goto error;
929 }
930
931 if (new_smp_sz < smp_sz) {
932 memprintf(err, "sample size %zu should be greater or equal to "
933 "%zu the maximum of the high ranges limits",
934 new_smp_sz, smp_sz);
935 goto error;
936 }
937 smp_sz = new_smp_sz;
938
939 /* Let's order <smp_rgs> array. */
940 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
941
942 logsrv->lb.smp_rgs = smp_rgs;
943 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
944 logsrv->lb.smp_sz = smp_sz;
945
946 cur_arg += 2;
947 }
Frédéric Lécailled803e472019-04-25 07:42:09 +0200948 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200949 /* parse the facility */
950 logsrv->facility = get_log_facility(args[cur_arg]);
951 if (logsrv->facility < 0) {
952 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
953 goto error;
954 }
955 cur_arg++;
956
957 /* parse the max syslog level (default: debug) */
958 logsrv->level = 7;
959 if (*(args[cur_arg])) {
960 logsrv->level = get_log_level(args[cur_arg]);
961 if (logsrv->level < 0) {
962 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
963 goto error;
964 }
965 cur_arg++;
966 }
967
968 /* parse the limit syslog level (default: emerg) */
969 logsrv->minlvl = 0;
970 if (*(args[cur_arg])) {
971 logsrv->minlvl = get_log_level(args[cur_arg]);
972 if (logsrv->minlvl < 0) {
973 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
974 goto error;
975 }
976 cur_arg++;
977 }
978
979 /* Too many args */
980 if (*(args[cur_arg])) {
981 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
982 goto error;
983 }
984
985 /* now, back to the address */
Willy Tarreauf3dc30f2019-08-30 14:18:44 +0200986 logsrv->type = LOG_TARGET_DGRAM;
Willy Tarreauc046d162019-08-30 15:24:59 +0200987 if (strncmp(args[1], "ring@", 5) == 0) {
Willy Tarreauc046d162019-08-30 15:24:59 +0200988 logsrv->addr.ss_family = AF_UNSPEC;
989 logsrv->type = LOG_TARGET_BUFFER;
Emeric Brun99c453d2020-05-25 15:01:04 +0200990 logsrv->sink = NULL;
991 logsrv->ring_name = strdup(args[1] + 5);
Willy Tarreauc046d162019-08-30 15:24:59 +0200992 goto done;
993 }
994
Emeric Brun94aab062021-04-02 10:41:36 +0200995 sk = str2sa_range(args[1], NULL, &port1, &port2, &fd, &proto,
996 err, NULL, NULL,
997 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 +0200998 if (!sk)
999 goto error;
Willy Tarreau89599262020-09-15 14:03:26 +02001000
1001 if (fd != -1)
1002 logsrv->type = LOG_TARGET_FD;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001003 logsrv->addr = *sk;
1004
1005 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001006 if (!port1)
1007 set_host_port(&logsrv->addr, SYSLOG_PORT);
1008 }
Emeric Brun9533a702021-04-02 10:13:43 +02001009
Emeric Brun26754902021-04-07 14:26:44 +02001010 if (proto && proto->ctrl_type == SOCK_STREAM) {
Emeric Brun94aab062021-04-02 10:41:36 +02001011 static unsigned long ring_ids;
1012
1013 /* Implicit sink buffer will be
1014 * initialized in post_check
1015 */
1016 logsrv->type = LOG_TARGET_BUFFER;
1017 logsrv->sink = NULL;
1018 /* compute uniq name for the ring */
1019 memprintf(&logsrv->ring_name, "ring#%lu", ++ring_ids);
1020 }
1021
Willy Tarreauc046d162019-08-30 15:24:59 +02001022 done:
Willy Tarreau2b718102021-04-21 07:32:39 +02001023 LIST_APPEND(logsrvs, &logsrv->list);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001024 return 1;
1025
1026 error:
Willy Tarreauae32ac72020-10-27 09:51:37 +01001027 free(smp_rgs);
Willy Tarreau211ea252022-03-17 19:47:33 +01001028 free_logsrv(logsrv);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001029 return 0;
1030}
1031
Amaury Denoyelle7b01a8d2021-03-29 10:29:07 +02001032
1033/*
Emeric Brun54648852020-07-06 15:54:06 +02001034 * returns log format, LOG_FORMAT_UNSPEC is return if not found.
Dragan Dosen1322d092015-09-22 16:05:32 +02001035 */
Emeric Brun54648852020-07-06 15:54:06 +02001036enum log_fmt get_log_format(const char *fmt)
Dragan Dosen1322d092015-09-22 16:05:32 +02001037{
Emeric Brun54648852020-07-06 15:54:06 +02001038 enum log_fmt format;
Dragan Dosen1322d092015-09-22 16:05:32 +02001039
1040 format = LOG_FORMATS - 1;
Emeric Brun54648852020-07-06 15:54:06 +02001041 while (format > 0 && log_formats[format].name
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001042 && strcmp(log_formats[format].name, fmt) != 0)
Dragan Dosen1322d092015-09-22 16:05:32 +02001043 format--;
1044
Emeric Brun54648852020-07-06 15:54:06 +02001045 /* Note: 0 is LOG_FORMAT_UNSPEC */
Dragan Dosen1322d092015-09-22 16:05:32 +02001046 return format;
1047}
1048
1049/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001050 * returns log level for <lev> or -1 if not found.
1051 */
1052int get_log_level(const char *lev)
1053{
1054 int level;
1055
1056 level = NB_LOG_LEVELS - 1;
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001057 while (level >= 0 && strcmp(log_levels[level], lev) != 0)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001058 level--;
1059
1060 return level;
1061}
1062
Willy Tarreaubaaee002006-06-26 02:48:02 +02001063/*
1064 * returns log facility for <fac> or -1 if not found.
1065 */
1066int get_log_facility(const char *fac)
1067{
1068 int facility;
1069
1070 facility = NB_LOG_FACILITIES - 1;
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001071 while (facility >= 0 && strcmp(log_facilities[facility], fac) != 0)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001072 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001073
Willy Tarreaubaaee002006-06-26 02:48:02 +02001074 return facility;
1075}
1076
William Lallemanda1cc3812012-02-08 16:38:44 +01001077/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001078 * Encode the string.
1079 *
1080 * When using the +E log format option, it will try to escape '"\]'
1081 * characters with '\' as prefix. The same prefix should not be used as
1082 * <escape>.
1083 */
1084static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001085 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001086 const char *string,
1087 struct logformat_node *node)
1088{
1089 if (node->options & LOG_OPT_ESC) {
1090 if (start < stop) {
1091 stop--; /* reserve one byte for the final '\0' */
1092 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001093 if (!ha_bit_test((unsigned char)(*string), map)) {
1094 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001095 *start++ = *string;
1096 else {
1097 if (start + 2 >= stop)
1098 break;
1099 *start++ = '\\';
1100 *start++ = *string;
1101 }
1102 }
1103 else {
1104 if (start + 3 >= stop)
1105 break;
1106 *start++ = escape;
1107 *start++ = hextab[(*string >> 4) & 15];
1108 *start++ = hextab[*string & 15];
1109 }
1110 string++;
1111 }
1112 *start = '\0';
1113 }
1114 }
1115 else {
1116 return encode_string(start, stop, escape, map, string);
1117 }
1118
1119 return start;
1120}
1121
1122/*
1123 * Encode the chunk.
1124 *
1125 * When using the +E log format option, it will try to escape '"\]'
1126 * characters with '\' as prefix. The same prefix should not be used as
1127 * <escape>.
1128 */
1129static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001130 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001131 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001132 struct logformat_node *node)
1133{
1134 char *str, *end;
1135
1136 if (node->options & LOG_OPT_ESC) {
1137 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001138 str = chunk->area;
1139 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001140
1141 stop--; /* reserve one byte for the final '\0' */
1142 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001143 if (!ha_bit_test((unsigned char)(*str), map)) {
1144 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001145 *start++ = *str;
1146 else {
1147 if (start + 2 >= stop)
1148 break;
1149 *start++ = '\\';
1150 *start++ = *str;
1151 }
1152 }
1153 else {
1154 if (start + 3 >= stop)
1155 break;
1156 *start++ = escape;
1157 *start++ = hextab[(*str >> 4) & 15];
1158 *start++ = hextab[*str & 15];
1159 }
1160 str++;
1161 }
1162 *start = '\0';
1163 }
1164 }
1165 else {
1166 return encode_chunk(start, stop, escape, map, chunk);
1167 }
1168
1169 return start;
1170}
1171
1172/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001173 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001174 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001175 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001176 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001177 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001178char *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 +01001179{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001180 if (size < 2)
1181 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001182
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001183 if (node->options & LOG_OPT_QUOTE) {
1184 *(dst++) = '"';
1185 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001186 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001187
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001188 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001189 if (++len > size)
1190 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001191 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001192 char *ret;
1193
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001194 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001195 if (ret == NULL || *ret != '\0')
1196 return NULL;
1197 len = ret - dst;
1198 }
1199 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001200 len = strlcpy2(dst, src, len);
1201 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001202
1203 size -= len;
1204 dst += len;
1205 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001206 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1207 if (size < 2)
1208 return NULL;
1209 *(dst++) = '-';
1210 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001211
1212 if (node->options & LOG_OPT_QUOTE) {
1213 if (size < 2)
1214 return NULL;
1215 *(dst++) = '"';
1216 }
1217
1218 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001219 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001220}
1221
Willy Tarreau26ffa852018-09-05 15:23:10 +02001222static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001223{
1224 return lf_text_len(dst, src, size, size, node);
1225}
1226
William Lallemand5f232402012-04-05 18:02:55 +02001227/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001228 * Write a IP address to the log string
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001229 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001230 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001231char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001232{
1233 char *ret = dst;
1234 int iret;
1235 char pn[INET6_ADDRSTRLEN];
1236
1237 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001238 unsigned char *addr = NULL;
1239 switch (sockaddr->sa_family) {
1240 case AF_INET:
1241 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1242 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1243 break;
1244 case AF_INET6:
1245 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1246 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1247 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1248 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1249 break;
1250 default:
1251 return NULL;
1252 }
William Lallemand5f232402012-04-05 18:02:55 +02001253 if (iret < 0 || iret > size)
1254 return NULL;
1255 ret += iret;
1256 } else {
1257 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1258 ret = lf_text(dst, pn, size, node);
1259 if (ret == NULL)
1260 return NULL;
1261 }
1262 return ret;
1263}
1264
1265/*
1266 * Write a port to the log
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001267 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001268 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001269char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001270{
1271 char *ret = dst;
1272 int iret;
1273
1274 if (node->options & LOG_OPT_HEXA) {
1275 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1276 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1277 if (iret < 0 || iret > size)
1278 return NULL;
1279 ret += iret;
1280 } else {
1281 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1282 if (ret == NULL)
1283 return NULL;
1284 }
1285 return ret;
1286}
1287
Emeric Brun54648852020-07-06 15:54:06 +02001288
1289/*
1290 * This function sends the syslog message using a printf format string. It
1291 * expects an LF-terminated message.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001292 */
Emeric Brun54648852020-07-06 15:54:06 +02001293void send_log(struct proxy *p, int level, const char *format, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001294{
Emeric Brun54648852020-07-06 15:54:06 +02001295 va_list argp;
1296 int data_len;
1297
1298 if (level < 0 || format == NULL || logline == NULL)
1299 return;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001300
Emeric Brun54648852020-07-06 15:54:06 +02001301 va_start(argp, format);
1302 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
1303 if (data_len < 0 || data_len > global.max_syslog_len)
1304 data_len = global.max_syslog_len;
1305 va_end(argp);
Willy Tarreaufe944602007-10-25 10:34:16 +02001306
Emeric Brun54648852020-07-06 15:54:06 +02001307 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1308 logline, data_len, default_rfc5424_sd_log_format, 2);
1309}
1310/*
1311 * This function builds a log header of given format using given
1312 * metadata, if format is set to LOF_FORMAT_UNSPEC, it tries
1313 * to determine format based on given metadas. It is useful
1314 * for log-forwarding to be able to forward any format without
1315 * settings.
1316 * This function returns a struct ist array of elements of the header
1317 * nbelem is set to the number of available elements.
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001318 * This function returns currently a maximum of NB_LOG_HDR_IST_ELEMENTS
Emeric Brun54648852020-07-06 15:54:06 +02001319 * elements.
1320 */
1321struct ist *build_log_header(enum log_fmt format, int level, int facility,
1322 struct ist *metadata, size_t *nbelem)
1323{
1324 static THREAD_LOCAL struct {
1325 struct ist ist_vector[NB_LOG_HDR_MAX_ELEMENTS];
1326 char timestamp_buffer[LOG_LEGACYTIME_LEN+1+1];
1327 time_t cur_legacy_time;
1328 char priority_buffer[6];
1329 } hdr_ctx = { .priority_buffer = "<<<<>" };
Willy Tarreaubaaee002006-06-26 02:48:02 +02001330
Emeric Brun54648852020-07-06 15:54:06 +02001331 struct tm logtime;
1332 int len;
1333 int fac_level = 0;
1334 time_t time = date.tv_sec;
Dragan Dosen43885c72015-10-01 13:18:13 +02001335
Emeric Brun54648852020-07-06 15:54:06 +02001336 *nbelem = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001337
Emeric Brun54648852020-07-06 15:54:06 +02001338
1339 if (format == LOG_FORMAT_UNSPEC) {
1340 format = LOG_FORMAT_RAW;
1341 if (metadata) {
1342 /* If a hostname is set, it appears we want to perform syslog
1343 * because only rfc5427 or rfc3164 support an hostname.
1344 */
1345 if (metadata[LOG_META_HOST].len) {
1346 /* If a rfc5424 compliant timestamp is used we consider
1347 * that output format is rfc5424, else legacy format
1348 * is used as specified default for local logs
1349 * in documentation.
1350 */
1351 if ((metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-')
1352 || (metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN))
1353 format = LOG_FORMAT_RFC5424;
1354 else
1355 format = LOG_FORMAT_RFC3164;
1356 }
Emeric Brun0237c4e2020-11-27 16:24:34 +01001357 else if (metadata[LOG_META_TAG].len) {
1358 /* Tag is present but no hostname, we should
Ilya Shipitsinf38a0182020-12-21 01:16:17 +05001359 * consider we try to emit a local log
Emeric Brun0237c4e2020-11-27 16:24:34 +01001360 * in legacy format (analog to RFC3164 but
1361 * with stripped hostname).
1362 */
1363 format = LOG_FORMAT_LOCAL;
1364 }
Emeric Brun54648852020-07-06 15:54:06 +02001365 else if (metadata[LOG_META_PRIO].len) {
1366 /* the source seems a parsed message
1367 * offering a valid level/prio prefix
1368 * so we consider this format.
1369 */
1370 format = LOG_FORMAT_PRIO;
1371 }
1372 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001373 }
1374
Emeric Brun54648852020-07-06 15:54:06 +02001375 /* prepare priority, stored into 1 single elem */
1376 switch (format) {
Emeric Brun0237c4e2020-11-27 16:24:34 +01001377 case LOG_FORMAT_LOCAL:
Emeric Brun54648852020-07-06 15:54:06 +02001378 case LOG_FORMAT_RFC3164:
1379 case LOG_FORMAT_RFC5424:
1380 case LOG_FORMAT_PRIO:
1381 fac_level = facility << 3;
1382 /* further format ignore the facility */
1383 /* fall through */
1384 case LOG_FORMAT_TIMED:
1385 case LOG_FORMAT_SHORT:
1386 fac_level += level;
1387 hdr_ctx.ist_vector[*nbelem].ptr = &hdr_ctx.priority_buffer[3]; /* last digit of the log level */
1388 do {
1389 *hdr_ctx.ist_vector[*nbelem].ptr = '0' + fac_level % 10;
1390 fac_level /= 10;
1391 hdr_ctx.ist_vector[*nbelem].ptr--;
1392 } while (fac_level && hdr_ctx.ist_vector[*nbelem].ptr > &hdr_ctx.priority_buffer[0]);
1393 *hdr_ctx.ist_vector[*nbelem].ptr = '<';
1394 hdr_ctx.ist_vector[(*nbelem)++].len = &hdr_ctx.priority_buffer[5] - hdr_ctx.ist_vector[0].ptr;
1395 break;
1396 case LOG_FORMAT_ISO:
1397 case LOG_FORMAT_RAW:
1398 break;
1399 case LOG_FORMAT_UNSPEC:
1400 case LOG_FORMATS:
1401 ABORT_NOW();
1402 }
Willy Tarreau094af4e2015-01-07 15:03:42 +01001403
William Lallemand2a4a44f2012-02-06 16:00:33 +01001404
Emeric Brun54648852020-07-06 15:54:06 +02001405 /* prepare timestamp, stored into a max of 4 elems */
1406 switch (format) {
Emeric Brun0237c4e2020-11-27 16:24:34 +01001407 case LOG_FORMAT_LOCAL:
Emeric Brun54648852020-07-06 15:54:06 +02001408 case LOG_FORMAT_RFC3164:
1409 /* rfc3164 ex: 'Jan 1 00:00:00 ' */
1410 if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
1411 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
1412 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001413 /* time is set, break immediately */
Emeric Brun54648852020-07-06 15:54:06 +02001414 break;
1415 }
1416 else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
1417 int month;
1418 char *timestamp = metadata[LOG_META_TIME].ptr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001419
Emeric Brun54648852020-07-06 15:54:06 +02001420 /* iso time always begins like this: '1970-01-01T00:00:00' */
Dragan Dosen1322d092015-09-22 16:05:32 +02001421
Emeric Brun54648852020-07-06 15:54:06 +02001422 /* compute month */
1423 month = 10*(timestamp[5] - '0') + (timestamp[6] - '0');
1424 if (month)
1425 month--;
1426 if (month <= 11) {
1427 /* builds log prefix ex: 'Jan 1 ' */
1428 len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
1429 "%s %c%c ", monthname[month],
1430 timestamp[8] != '0' ? timestamp[8] : ' ',
1431 timestamp[9]);
1432 /* we reused the timestamp_buffer, signal that it does not
1433 * contain local time anymore
1434 */
1435 hdr_ctx.cur_legacy_time = 0;
1436 if (len == 7) {
1437 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
1438 /* adds 'HH:MM:SS' from iso time */
1439 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[11], 8);
1440 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1441 /* we successfully reuse iso time, we can break */
1442 break;
1443 }
1444 }
1445 /* Failed to reuse isotime time, fallback to local legacy time */
1446 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001447
Emeric Brun54648852020-07-06 15:54:06 +02001448 if (unlikely(time != hdr_ctx.cur_legacy_time)) {
1449 /* re-builds timestamp from the current local time */
1450 get_localtime(time, &logtime);
1451
1452 len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
1453 "%s %2d %02d:%02d:%02d ",
1454 monthname[logtime.tm_mon],
1455 logtime.tm_mday, logtime.tm_hour, logtime.tm_min, logtime.tm_sec);
1456 if (len != LOG_LEGACYTIME_LEN+1)
1457 hdr_ctx.cur_legacy_time = 0;
1458 else
1459 hdr_ctx.cur_legacy_time = time;
1460 }
1461 if (likely(hdr_ctx.cur_legacy_time))
1462 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], LOG_LEGACYTIME_LEN+1);
1463 else
1464 hdr_ctx.ist_vector[(*nbelem)++] = ist2("Jan 1 00:00:00 ", LOG_LEGACYTIME_LEN+1);
1465 break;
1466 case LOG_FORMAT_RFC5424:
1467 /* adds rfc5425 version prefix */
1468 hdr_ctx.ist_vector[(*nbelem)++] = ist2("1 ", 2);
1469 if (metadata && metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-') {
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001470 /* submitted len is NILVALUE, it is a valid timestamp for rfc5425 */
Emeric Brun54648852020-07-06 15:54:06 +02001471 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
1472 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1473 break;
1474 }
1475 /* let continue as 'timed' and 'iso' format for usual timestamp */
1476 /* fall through */
1477 case LOG_FORMAT_TIMED:
1478 case LOG_FORMAT_ISO:
1479 /* ISO format ex: '1900:01:01T12:00:00.123456Z'
1480 * '1900:01:01T14:00:00+02:00'
1481 * '1900:01:01T10:00:00.123456-02:00'
1482 */
1483 if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
1484 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
1485 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001486 /* time is set, break immediately */
Emeric Brun54648852020-07-06 15:54:06 +02001487 break;
1488 }
1489 else if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
1490 int month;
1491 char *timestamp = metadata[LOG_META_TIME].ptr;
1492
1493 for (month = 0; month < 12; month++)
1494 if (!memcmp(monthname[month], timestamp, 3))
1495 break;
Dragan Dosen1322d092015-09-22 16:05:32 +02001496
Emeric Brun54648852020-07-06 15:54:06 +02001497 if (month < 12) {
1498
1499 /* get local time to retrieve year */
1500 get_localtime(time, &logtime);
1501
1502 /* year seems changed since log */
1503 if (logtime.tm_mon < month)
1504 logtime.tm_year--;
1505
1506 /* builds rfc5424 prefix ex: '1900-01-01T' */
1507 len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
1508 "%4d-%02d-%c%cT",
1509 logtime.tm_year+1900, month+1,
1510 timestamp[4] != ' ' ? timestamp[4] : '0',
1511 timestamp[5]);
1512
1513 /* we reused the timestamp_buffer, signal that it does not
1514 * contain local time anymore
1515 */
1516 hdr_ctx.cur_legacy_time = 0;
1517 if (len == 11) {
1518 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
1519 /* adds HH:MM:SS from legacy timestamp */
1520 hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[7], 8);
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05001521 /* skip secfraq because it is optional */
Emeric Brun54648852020-07-06 15:54:06 +02001522 /* according to rfc: -00:00 means we don't know the timezone */
1523 hdr_ctx.ist_vector[(*nbelem)++] = ist2("-00:00 ", 7);
1524 /* we successfully reuse legacy time, we can break */
1525 break;
1526 }
1527 }
1528 /* Failed to reuse legacy time, fallback to local iso time */
1529 }
1530 hdr_ctx.ist_vector[(*nbelem)++] = ist2(timeofday_as_iso_us(1), LOG_ISOTIME_MAXLEN + 1);
1531 break;
1532 case LOG_FORMAT_PRIO:
1533 case LOG_FORMAT_SHORT:
1534 case LOG_FORMAT_RAW:
1535 break;
1536 case LOG_FORMAT_UNSPEC:
1537 case LOG_FORMATS:
1538 ABORT_NOW();
Dragan Dosen1322d092015-09-22 16:05:32 +02001539 }
1540
Emeric Brun54648852020-07-06 15:54:06 +02001541 /* prepare other meta data, stored into a max of 10 elems */
1542 switch (format) {
1543 case LOG_FORMAT_RFC3164:
1544 if (metadata && metadata[LOG_META_HOST].len) {
1545 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
1546 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1547 }
Emeric Brun0237c4e2020-11-27 16:24:34 +01001548 else /* the caller MUST fill the hostname, this field is mandatory */
Emeric Brun54648852020-07-06 15:54:06 +02001549 hdr_ctx.ist_vector[(*nbelem)++] = ist2("localhost ", 10);
Emeric Brun0237c4e2020-11-27 16:24:34 +01001550 /* fall through */
1551 case LOG_FORMAT_LOCAL:
Emeric Brun54648852020-07-06 15:54:06 +02001552 if (!metadata || !metadata[LOG_META_TAG].len)
1553 break;
Dragan Dosen1322d092015-09-22 16:05:32 +02001554
Emeric Brun54648852020-07-06 15:54:06 +02001555 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
1556 if (metadata[LOG_META_PID].len) {
1557 hdr_ctx.ist_vector[(*nbelem)++] = ist2("[", 1);
1558 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
1559 hdr_ctx.ist_vector[(*nbelem)++] = ist2("]", 1);
1560 }
1561 hdr_ctx.ist_vector[(*nbelem)++] = ist2(": ", 2);
1562 break;
1563 case LOG_FORMAT_RFC5424:
1564 if (metadata && metadata[LOG_META_HOST].len) {
1565 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
1566 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1567 }
1568 else
1569 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
Dragan Dosen1322d092015-09-22 16:05:32 +02001570
Emeric Brun54648852020-07-06 15:54:06 +02001571 if (metadata && metadata[LOG_META_TAG].len) {
1572 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
1573 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1574 }
1575 else
1576 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001577
Emeric Brun54648852020-07-06 15:54:06 +02001578 if (metadata && metadata[LOG_META_PID].len) {
1579 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
1580 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1581 }
1582 else
1583 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001584
Emeric Brun54648852020-07-06 15:54:06 +02001585 if (metadata && metadata[LOG_META_MSGID].len) {
1586 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_MSGID];
1587 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1588 }
1589 else
1590 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001591
Emeric Brun54648852020-07-06 15:54:06 +02001592 if (metadata && metadata[LOG_META_STDATA].len) {
1593 hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_STDATA];
1594 hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
1595 }
1596 else
1597 hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
1598 break;
1599 case LOG_FORMAT_PRIO:
1600 case LOG_FORMAT_SHORT:
1601 case LOG_FORMAT_TIMED:
1602 case LOG_FORMAT_ISO:
1603 case LOG_FORMAT_RAW:
1604 break;
1605 case LOG_FORMAT_UNSPEC:
1606 case LOG_FORMATS:
1607 ABORT_NOW();
1608 }
1609
1610 return hdr_ctx.ist_vector;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001611}
1612
1613/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001614 * This function sends a syslog message to <logsrv>.
Emeric Brun54648852020-07-06 15:54:06 +02001615 * The argument <metadata> MUST be an array of size
1616 * LOG_META_FIELDS*sizeof(struct ist) containing data to build the header.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001617 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001618 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001619 */
Emeric Brun54648852020-07-06 15:54:06 +02001620static 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 +01001621{
Emeric Brun54648852020-07-06 15:54:06 +02001622 static THREAD_LOCAL struct iovec iovec[NB_LOG_HDR_MAX_ELEMENTS+1+1] = { }; /* header elements + message + LF */
Christopher Fauletf8188c62017-06-02 16:20:16 +02001623 static THREAD_LOCAL struct msghdr msghdr = {
1624 //.msg_iov = iovec,
Emeric Brun54648852020-07-06 15:54:06 +02001625 .msg_iovlen = NB_LOG_HDR_MAX_ELEMENTS+2
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001626 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001627 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1628 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001629 int *plogfd;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001630 int sent;
Emeric Brun54648852020-07-06 15:54:06 +02001631 size_t nbelem;
1632 struct ist *msg_header = NULL;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001633
1634 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001635
Emeric Brunfa9d7802020-05-28 14:21:33 +02001636 /* historically some messages used to already contain the trailing LF
1637 * or Zero. Let's remove all trailing LF or Zero
1638 */
Emeric Brun54648852020-07-06 15:54:06 +02001639 while (size && (message[size-1] == '\n' || (message[size-1] == 0)))
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001640 size--;
1641
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001642 if (logsrv->type == LOG_TARGET_BUFFER) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001643 plogfd = NULL;
Emeric Brune709e1e2020-05-06 17:23:59 +02001644 goto send;
Willy Tarreauc046d162019-08-30 15:24:59 +02001645 }
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001646 else if (logsrv->addr.ss_family == AF_CUST_EXISTING_FD) {
1647 /* the socket's address is a file descriptor */
1648 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1649 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001650 else if (logsrv->addr.ss_family == AF_UNIX)
1651 plogfd = &logfdunix;
1652 else
1653 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001654
Willy Tarreauc046d162019-08-30 15:24:59 +02001655 if (plogfd && unlikely(*plogfd < 0)) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001656 /* socket not successfully initialized yet */
1657 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1658 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1659 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001660
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001661 if (!once) {
1662 once = 1; /* note: no need for atomic ops here */
1663 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1664 nblogger, strerror(errno), errno);
1665 }
1666 return;
1667 } else {
1668 /* we don't want to receive anything on this socket */
1669 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1670 /* does nothing under Linux, maybe needed for others */
1671 shutdown(*plogfd, SHUT_RD);
1672 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1673 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001674 }
1675
Emeric Brun54648852020-07-06 15:54:06 +02001676 msg_header = build_log_header(logsrv->format, level, facility, metadata, &nbelem);
1677 send:
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001678 if (logsrv->type == LOG_TARGET_BUFFER) {
Emeric Brun54648852020-07-06 15:54:06 +02001679 struct ist msg;
1680
1681 msg = ist2(message, size);
Tim Duesterhus2471f5c2021-11-08 09:05:01 +01001682 msg = isttrim(msg, logsrv->maxlen);
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001683
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001684 sent = sink_write(logsrv->sink, &msg, 1, level, logsrv->facility, metadata);
1685 }
1686 else if (logsrv->addr.ss_family == AF_CUST_EXISTING_FD) {
1687 struct ist msg;
1688
1689 msg = ist2(message, size);
Tim Duesterhus2471f5c2021-11-08 09:05:01 +01001690 msg = isttrim(msg, logsrv->maxlen);
Willy Tarreaua5b325f2020-09-04 16:44:20 +02001691
1692 sent = fd_write_frag_line(*plogfd, logsrv->maxlen, msg_header, nbelem, &msg, 1, 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001693 }
1694 else {
Emeric Brun54648852020-07-06 15:54:06 +02001695 int i = 0;
1696 int totlen = logsrv->maxlen;
1697
1698 for (i = 0 ; i < nbelem ; i++ ) {
1699 iovec[i].iov_base = msg_header[i].ptr;
1700 iovec[i].iov_len = msg_header[i].len;
1701 if (totlen <= iovec[i].iov_len) {
1702 iovec[i].iov_len = totlen;
1703 totlen = 0;
1704 break;
1705 }
1706 totlen -= iovec[i].iov_len;
1707 }
1708 if (totlen) {
1709 iovec[i].iov_base = message;
1710 iovec[i].iov_len = size;
1711 if (totlen <= iovec[i].iov_len)
1712 iovec[i].iov_len = totlen;
1713 i++;
1714 }
1715 iovec[i].iov_base = "\n"; /* insert a \n at the end of the message */
1716 iovec[i].iov_len = 1;
1717 i++;
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001718
Emeric Brun54648852020-07-06 15:54:06 +02001719 msghdr.msg_iovlen = i;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001720 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1721 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001722
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001723 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1724 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001725
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001726 if (sent < 0) {
1727 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001728
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001729 if (errno == EAGAIN)
Willy Tarreau4781b152021-04-06 13:53:36 +02001730 _HA_ATOMIC_INC(&dropped_logs);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001731 else if (!once) {
1732 once = 1; /* note: no need for atomic ops here */
1733 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1734 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001735 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001736 }
1737}
Dragan Dosen59cee972015-09-19 22:09:02 +02001738
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001739/*
1740 * This function sends a syslog message.
1741 * It doesn't care about errors nor does it report them.
Emeric Brun54648852020-07-06 15:54:06 +02001742 * The argument <metadata> MUST be an array of size
1743 * LOG_META_FIELDS*sizeof(struct ist) containing
1744 * data to build the header.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001745 */
Emeric Brun54648852020-07-06 15:54:06 +02001746void process_send_log(struct list *logsrvs, int level, int facility,
1747 struct ist *metadata, char *message, size_t size)
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001748{
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001749 struct logsrv *logsrv;
1750 int nblogger;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001751
1752 /* Send log messages to syslog server. */
1753 nblogger = 0;
1754 list_for_each_entry(logsrv, logsrvs, list) {
Emeric Brun2f4cc282020-07-10 15:47:11 +02001755 int in_range = 1;
Frédéric Lécailled803e472019-04-25 07:42:09 +02001756
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001757 /* we can filter the level of the messages that are sent to each logger */
1758 if (level > logsrv->level)
1759 continue;
1760
Frédéric Lécailled803e472019-04-25 07:42:09 +02001761 if (logsrv->lb.smp_rgs) {
1762 struct smp_log_range *curr_rg;
1763
1764 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1765 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1766 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1767 if (in_range) {
1768 /* Let's consume this range. */
1769 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1770 if (!curr_rg->curr_idx) {
1771 /* If consumed, let's select the next range. */
1772 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1773 }
1774 }
1775 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1776 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1777 }
1778 if (in_range)
Emeric Brun54648852020-07-06 15:54:06 +02001779 __do_send_log(logsrv, ++nblogger, MAX(level, logsrv->minlvl),
1780 (facility == -1) ? logsrv->facility : facility,
1781 metadata, message, size);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001782 }
1783}
1784
Emeric Brun54648852020-07-06 15:54:06 +02001785/*
1786 * This function sends a syslog message.
1787 * It doesn't care about errors nor does it report them.
1788 * The arguments <sd> and <sd_size> are used for the structured-data part
1789 * in RFC5424 formatted syslog messages.
1790 */
1791void __send_log(struct list *logsrvs, struct buffer *tagb, int level,
1792 char *message, size_t size, char *sd, size_t sd_size)
1793{
1794 static THREAD_LOCAL pid_t curr_pid;
1795 static THREAD_LOCAL char pidstr[16];
1796 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
1797
1798 if (logsrvs == NULL) {
1799 if (!LIST_ISEMPTY(&global.logsrvs)) {
1800 logsrvs = &global.logsrvs;
1801 }
1802 }
1803 if (!logsrvs || LIST_ISEMPTY(logsrvs))
1804 return;
1805
1806 if (!metadata[LOG_META_HOST].len) {
1807 if (global.log_send_hostname)
Tim Duesterhus77508502022-03-15 13:11:06 +01001808 metadata[LOG_META_HOST] = ist(global.log_send_hostname);
Emeric Brun54648852020-07-06 15:54:06 +02001809 }
1810
1811 if (!tagb || !tagb->area)
1812 tagb = &global.log_tag;
1813
1814 if (tagb)
1815 metadata[LOG_META_TAG] = ist2(tagb->area, tagb->data);
1816
1817 if (unlikely(curr_pid != getpid()))
1818 metadata[LOG_META_PID].len = 0;
1819
1820 if (!metadata[LOG_META_PID].len) {
1821 curr_pid = getpid();
1822 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1823 metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
1824 }
1825
1826 metadata[LOG_META_STDATA] = ist2(sd, sd_size);
1827
1828 /* Remove trailing space of structured data */
1829 while (metadata[LOG_META_STDATA].len && metadata[LOG_META_STDATA].ptr[metadata[LOG_META_STDATA].len-1] == ' ')
1830 metadata[LOG_META_STDATA].len--;
1831
1832 return process_send_log(logsrvs, level, -1, metadata, message, size);
1833}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001834
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001835const 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 +01001836const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1837 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1838 Set-cookie Updated, unknown, unknown */
1839
William Lallemand1d705562012-03-12 12:46:41 +01001840/*
1841 * try to write a character if there is enough space, or goto out
1842 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001843#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001844 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001845 *(tmplog++) = (x); \
1846 } else { \
1847 goto out; \
1848 } \
1849 } while(0)
1850
Dragan Dosen835b9212016-02-12 13:23:03 +01001851
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001852/* Initializes some log data at boot */
1853static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001854{
1855 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001856 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001857
1858 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1859 * inside PARAM-VALUE should be escaped with '\' as prefix.
1860 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1861 * details.
1862 */
1863 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1864
1865 tmp = "\"\\]";
1866 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001867 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001868 tmp++;
1869 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001870
1871 /* initialize the log header encoding map : '{|}"#' should be encoded with
1872 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1873 * URL encoding only requires '"', '#' to be encoded as well as non-
1874 * printable characters above.
1875 */
1876 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1877 memset(url_encode_map, 0, sizeof(url_encode_map));
1878 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001879 ha_bit_set(i, hdr_encode_map);
1880 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001881 }
1882 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001883 ha_bit_set(i, hdr_encode_map);
1884 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001885 }
1886
1887 tmp = "\"#{|}";
1888 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001889 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001890 tmp++;
1891 }
1892
1893 tmp = "\"#";
1894 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001895 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001896 tmp++;
1897 }
1898
1899 /* initialize the http header encoding map. The draft httpbis define the
1900 * header content as:
1901 *
1902 * HTTP-message = start-line
1903 * *( header-field CRLF )
1904 * CRLF
1905 * [ message-body ]
1906 * header-field = field-name ":" OWS field-value OWS
1907 * field-value = *( field-content / obs-fold )
1908 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1909 * obs-fold = CRLF 1*( SP / HTAB )
1910 * field-vchar = VCHAR / obs-text
1911 * VCHAR = %x21-7E
1912 * obs-text = %x80-FF
1913 *
1914 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1915 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001916 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001917 */
1918 memset(http_encode_map, 0, sizeof(http_encode_map));
1919 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001920 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001921 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001922 ha_bit_set(i, http_encode_map);
1923 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001924}
William Lallemand1d705562012-03-12 12:46:41 +01001925
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001926INITCALL0(STG_PREPARE, init_log);
1927
Christopher Faulet0132d062017-07-26 15:33:35 +02001928/* Initialize log buffers used for syslog messages */
1929int init_log_buffers()
1930{
Christopher Faulet0132d062017-07-26 15:33:35 +02001931 logline = my_realloc2(logline, global.max_syslog_len + 1);
1932 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
Emeric Brun54648852020-07-06 15:54:06 +02001933 if (!logline || !logline_rfc5424)
Christopher Faulet0132d062017-07-26 15:33:35 +02001934 return 0;
1935 return 1;
1936}
1937
1938/* Deinitialize log buffers used for syslog messages */
1939void deinit_log_buffers()
1940{
Christopher Faulet0132d062017-07-26 15:33:35 +02001941 free(logline);
1942 free(logline_rfc5424);
Christopher Faulet0132d062017-07-26 15:33:35 +02001943 logline = NULL;
1944 logline_rfc5424 = NULL;
1945}
1946
Willy Tarreaudf974472012-12-28 02:44:01 +01001947/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1948 * <maxsize> characters. Returns the size of the output string in characters,
1949 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001950 * is not zero. It requires a valid session and optionally a stream. If the
1951 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001952 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001953int 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 +02001954{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001955 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001956 struct proxy *be;
1957 struct http_txn *txn;
1958 const struct strm_logs *logs;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02001959 struct connection *fe_conn, *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001960 unsigned int s_flags;
1961 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001962 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001963 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001964 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001965 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001966 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001967 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001968 int t_request;
1969 int hdr;
1970 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001971 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001972 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001973 char *ret;
1974 int iret;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02001975 int status;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001976 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001977 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001978 struct strm_logs tmp_strm_log;
Maciej Zdebfcdfd852020-11-30 18:27:47 +00001979 struct ist path;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001980 struct http_uri_parser parser;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001981
William Lallemandbddd4fd2012-02-27 11:23:10 +01001982 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001983
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001984 if (likely(s)) {
1985 be = s->be;
1986 txn = s->txn;
Christopher Faulet95a61e82021-12-22 14:22:03 +01001987 be_conn = cs_conn(s->csb);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02001988 status = (txn ? txn->status : 0);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001989 s_flags = s->flags;
1990 uniq_id = s->uniq_id;
1991 logs = &s->logs;
1992 } else {
1993 /* we have no stream so we first need to initialize a few
1994 * things that are needed later. We do increment the request
1995 * ID so that it's uniquely assigned to this request just as
1996 * if the request had reached the point of being processed.
1997 * A request error is reported as it's the only element we have
1998 * here and which justifies emitting such a log.
1999 */
Christopher Fauletfd818482021-04-14 14:01:41 +02002000 be = ((obj_type(sess->origin) == OBJ_TYPE_CHECK) ? __objt_check(sess->origin)->proxy : fe);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002001 txn = NULL;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002002 fe_conn = objt_conn(sess->origin);
Christopher Fauletfd818482021-04-14 14:01:41 +02002003 be_conn = ((obj_type(sess->origin) == OBJ_TYPE_CHECK) ? cs_conn(__objt_check(sess->origin)->cs) : NULL);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002004 status = 0;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002005 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Willy Tarreau18515722021-04-06 11:57:41 +02002006 uniq_id = _HA_ATOMIC_FETCH_ADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002007
2008 /* prepare a valid log structure */
2009 tmp_strm_log.tv_accept = sess->tv_accept;
2010 tmp_strm_log.accept_date = sess->accept_date;
2011 tmp_strm_log.t_handshake = sess->t_handshake;
Christopher Fauletdd789212020-09-30 15:10:07 +02002012 tmp_strm_log.t_idle = (sess->t_idle >= 0 ? sess->t_idle : 0);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002013 tv_zero(&tmp_strm_log.tv_request);
2014 tmp_strm_log.t_queue = -1;
2015 tmp_strm_log.t_connect = -1;
2016 tmp_strm_log.t_data = -1;
2017 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
2018 tmp_strm_log.bytes_in = 0;
2019 tmp_strm_log.bytes_out = 0;
2020 tmp_strm_log.prx_queue_pos = 0;
2021 tmp_strm_log.srv_queue_pos = 0;
2022
2023 logs = &tmp_strm_log;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002024
2025 if ((fe->mode == PR_MODE_HTTP) && fe_conn && fe_conn->mux && fe_conn->mux->ctl) {
Christopher Fauleta015b3e2021-09-28 11:36:28 +02002026 enum mux_exit_status es = fe_conn->mux->ctl(fe_conn, MUX_EXIT_STATUS, &status);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002027
2028 switch (es) {
2029 case MUX_ES_SUCCESS:
2030 break;
2031 case MUX_ES_INVALID_ERR:
Christopher Fauleta015b3e2021-09-28 11:36:28 +02002032 status = (status ? status : 400);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002033 if ((fe_conn->flags & CO_FL_ERROR) || conn_xprt_read0_pending(fe_conn))
2034 s_flags = SF_ERR_CLICL | SF_FINST_R;
2035 else
2036 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
2037 break;
2038 case MUX_ES_TOUT_ERR:
Christopher Fauleta015b3e2021-09-28 11:36:28 +02002039 status = (status ? status : 408);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002040 s_flags = SF_ERR_CLITO | SF_FINST_R;
2041 break;
Christopher Faulet142dd332020-12-07 11:24:37 +01002042 case MUX_ES_NOTIMPL_ERR:
Christopher Fauleta015b3e2021-09-28 11:36:28 +02002043 status = (status ? status : 501);
Christopher Faulet142dd332020-12-07 11:24:37 +01002044 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
2045 break;
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002046 case MUX_ES_INTERNAL_ERR:
Christopher Fauleta015b3e2021-09-28 11:36:28 +02002047 status = (status ? status : 500);
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002048 s_flags = SF_ERR_INTERNAL | SF_FINST_R;
2049 break;
2050 default:
2051 break;
2052 }
2053 }
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002054 }
2055
William Lallemandbddd4fd2012-02-27 11:23:10 +01002056 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002057 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2058 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01002059
William Lallemand1d705562012-03-12 12:46:41 +01002060 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02002061
William Lallemandbddd4fd2012-02-27 11:23:10 +01002062 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01002063 if (LIST_ISEMPTY(list_format))
2064 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002065
William Lallemand1d705562012-03-12 12:46:41 +01002066 list_for_each_entry(tmp, list_format, list) {
Christopher Faulet52b28d22021-10-27 11:58:05 +02002067#ifdef USE_OPENSSL
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002068 struct connection *conn;
Christopher Faulet52b28d22021-10-27 11:58:05 +02002069#endif
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002070 const struct sockaddr_storage *addr;
Willy Tarreau4f653562012-10-12 19:48:16 +02002071 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01002072 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02002073 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01002074
Willy Tarreauc8368452012-12-21 00:09:23 +01002075 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01002076 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002077 if (!last_isspace) {
2078 LOGCHAR(' ');
2079 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002080 }
2081 break;
2082
William Lallemand1d705562012-03-12 12:46:41 +01002083 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002084 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002085 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002086 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002087 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002088 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002089 last_isspace = 0;
2090 break;
2091
Willy Tarreauc8368452012-12-21 00:09:23 +01002092 case LOG_FMT_EXPR: // sample expression, may be request or response
2093 key = NULL;
Christopher Faulet5f940702020-04-06 10:40:02 +02002094 if (tmp->options & LOG_OPT_REQ_CAP)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002095 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Willy Tarreau20b7a0f2021-09-03 08:53:29 +02002096
Christopher Faulet5f940702020-04-06 10:40:02 +02002097 if (!key && (tmp->options & LOG_OPT_RES_CAP))
Adis Nezirovic79beb242015-07-06 15:41:02 +02002098 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Willy Tarreau20b7a0f2021-09-03 08:53:29 +02002099
2100 if (!key && !(tmp->options & (LOG_OPT_REQ_CAP|LOG_OPT_RES_CAP))) // cfg, cli
2101 key = sample_fetch_as_type(be, sess, s, SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
2102
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002103 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002104 ret = lf_encode_chunk(tmplog, dst + maxsize,
2105 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002106 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002107 ret = lf_text_len(tmplog,
2108 key ? key->data.u.str.area : NULL,
2109 key ? key->data.u.str.data : 0,
2110 dst + maxsize - tmplog,
2111 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002112 if (ret == 0)
2113 goto out;
2114 tmplog = ret;
2115 last_isspace = 0;
2116 break;
2117
Willy Tarreau2beef582012-12-20 17:22:52 +01002118 case LOG_FMT_CLIENTIP: // %ci
Christopher Faulet02fc86e2021-12-23 13:32:42 +01002119 addr = (s ? si_src(cs_si(s->csf)) : sess_src(sess));
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002120 if (addr)
2121 ret = lf_ip(tmplog, (struct sockaddr *)addr, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002122 else
2123 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002124
William Lallemand1d705562012-03-12 12:46:41 +01002125 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002126 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002127 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002128 last_isspace = 0;
2129 break;
2130
Willy Tarreau2beef582012-12-20 17:22:52 +01002131 case LOG_FMT_CLIENTPORT: // %cp
Christopher Faulet02fc86e2021-12-23 13:32:42 +01002132 addr = (s ? si_src(cs_si(s->csf)) : sess_src(sess));
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002133 if (addr) {
Christopher Faulet1ccbe122021-11-15 11:31:08 +01002134 /* sess->listener is always defined when the session's owner is an inbound connections */
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002135 if (addr->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002136 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002137 else
2138 ret = lf_port(tmplog, (struct sockaddr *)addr, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002139 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002140 else
2141 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2142
William Lallemand5f232402012-04-05 18:02:55 +02002143 if (ret == NULL)
2144 goto out;
2145 tmplog = ret;
2146 last_isspace = 0;
2147 break;
2148
Willy Tarreau2beef582012-12-20 17:22:52 +01002149 case LOG_FMT_FRONTENDIP: // %fi
Christopher Faulet02fc86e2021-12-23 13:32:42 +01002150 addr = (s ? si_dst(cs_si(s->csf)) : sess_dst(sess));
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002151 if (addr)
2152 ret = lf_ip(tmplog, (struct sockaddr *)addr, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002153 else
2154 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2155
William Lallemand1d705562012-03-12 12:46:41 +01002156 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002157 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002158 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002159 last_isspace = 0;
2160 break;
2161
Willy Tarreau2beef582012-12-20 17:22:52 +01002162 case LOG_FMT_FRONTENDPORT: // %fp
Christopher Faulet02fc86e2021-12-23 13:32:42 +01002163 addr = (s ? si_dst(cs_si(s->csf)) : sess_dst(sess));
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002164 if (addr) {
Christopher Faulet1ccbe122021-11-15 11:31:08 +01002165 /* sess->listener is always defined when the session's owner is an inbound connections */
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002166 if (addr->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002167 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002168 else
Christopher Fauletf9c4d8d2021-10-22 17:43:22 +02002169 ret = lf_port(tmplog, (struct sockaddr *)addr, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002170 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002171 else
2172 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2173
William Lallemand5f232402012-04-05 18:02:55 +02002174 if (ret == NULL)
2175 goto out;
2176 tmplog = ret;
2177 last_isspace = 0;
2178 break;
2179
Willy Tarreau2beef582012-12-20 17:22:52 +01002180 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002181 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002182 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002183 else
2184 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2185
William Lallemand1d705562012-03-12 12:46:41 +01002186 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002187 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002188 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002189 last_isspace = 0;
2190 break;
2191
Willy Tarreau2beef582012-12-20 17:22:52 +01002192 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002193 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002194 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002195 else
2196 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2197
William Lallemand5f232402012-04-05 18:02:55 +02002198 if (ret == NULL)
2199 goto out;
2200 tmplog = ret;
2201 last_isspace = 0;
2202 break;
2203
Willy Tarreau2beef582012-12-20 17:22:52 +01002204 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002205 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002206 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002207 else
2208 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2209
William Lallemand5f232402012-04-05 18:02:55 +02002210 if (ret == NULL)
2211 goto out;
2212 tmplog = ret;
2213 last_isspace = 0;
2214 break;
2215
Willy Tarreau2beef582012-12-20 17:22:52 +01002216 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002217 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002218 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002219 else
2220 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2221
William Lallemand1d705562012-03-12 12:46:41 +01002222 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002223 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002224 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002225 last_isspace = 0;
2226 break;
2227
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002228 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002229 get_localtime(logs->accept_date.tv_sec, &tm);
2230 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002231 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002232 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002233 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002234 last_isspace = 0;
2235 break;
2236
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002237 case LOG_FMT_tr: // %tr = start of request date
2238 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002239 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 +02002240 get_localtime(tv.tv_sec, &tm);
2241 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2242 if (ret == NULL)
2243 goto out;
2244 tmplog = ret;
2245 last_isspace = 0;
2246 break;
2247
2248 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002249 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002250 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002251 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002252 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002253 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002254 last_isspace = 0;
2255 break;
2256
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002257 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002258 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 +02002259 get_gmtime(tv.tv_sec, &tm);
2260 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2261 if (ret == NULL)
2262 goto out;
2263 tmplog = ret;
2264 last_isspace = 0;
2265 break;
2266
2267 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002268 get_localtime(logs->accept_date.tv_sec, &tm);
2269 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002270 if (ret == NULL)
2271 goto out;
2272 tmplog = ret;
2273 last_isspace = 0;
2274 break;
2275
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002276 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002277 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 +02002278 get_localtime(tv.tv_sec, &tm);
2279 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2280 if (ret == NULL)
2281 goto out;
2282 tmplog = ret;
2283 last_isspace = 0;
2284 break;
2285
William Lallemand5f232402012-04-05 18:02:55 +02002286 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002287 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002288 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002289 if (iret < 0 || iret > dst + maxsize - tmplog)
2290 goto out;
2291 last_isspace = 0;
2292 tmplog += iret;
2293 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002294 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002295 if (ret == NULL)
2296 goto out;
2297 tmplog = ret;
2298 last_isspace = 0;
2299 }
2300 break;
2301
William Lallemand1d705562012-03-12 12:46:41 +01002302 case LOG_FMT_MS: // %ms
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, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
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 {
2310 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002311 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002312 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002313 tmplog, 4);
2314 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002315 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002316 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002317 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002318 }
2319 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002320
William Lallemand1d705562012-03-12 12:46:41 +01002321 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002322 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002323 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002324 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002325 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002326 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002327 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002328 break;
2329
Willy Tarreau773d65f2012-10-12 14:56:11 +02002330 case LOG_FMT_FRONTEND_XPRT: // %ft
2331 src = fe->id;
2332 if (tmp->options & LOG_OPT_QUOTE)
2333 LOGCHAR('"');
2334 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2335 if (iret == 0)
2336 goto out;
2337 tmplog += iret;
Christopher Faulet1ccbe122021-11-15 11:31:08 +01002338
2339 /* sess->listener may be undefined if the session's owner is a health-check */
Willy Tarreau807a3a52022-04-12 08:08:33 +02002340 if (sess->listener && sess->listener->bind_conf->xprt->get_ssl_sock_ctx)
Willy Tarreau773d65f2012-10-12 14:56:11 +02002341 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002342 if (tmp->options & LOG_OPT_QUOTE)
2343 LOGCHAR('"');
2344 last_isspace = 0;
2345 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002346#ifdef USE_OPENSSL
2347 case LOG_FMT_SSL_CIPHER: // %sslc
2348 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002349 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002350 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002351 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002352 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002353 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2354 if (ret == NULL)
2355 goto out;
2356 tmplog = ret;
2357 last_isspace = 0;
2358 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002359
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002360 case LOG_FMT_SSL_VERSION: // %sslv
2361 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002362 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002363 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002364 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002365 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002366 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2367 if (ret == NULL)
2368 goto out;
2369 tmplog = ret;
2370 last_isspace = 0;
2371 break;
2372#endif
William Lallemand1d705562012-03-12 12:46:41 +01002373 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002374 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002375 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002376 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002377 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002378 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002379 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002380 break;
2381
William Lallemand1d705562012-03-12 12:46:41 +01002382 case LOG_FMT_SERVER: // %s
Christopher Fauletfd818482021-04-14 14:01:41 +02002383 switch (obj_type(s ? s->target : sess->origin)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002384 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002385 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002386 break;
2387 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002388 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002389 break;
Christopher Fauletfd818482021-04-14 14:01:41 +02002390 case OBJ_TYPE_CHECK:
2391 src = (__objt_check(sess->origin)->server
2392 ? __objt_check(sess->origin)->server->id
2393 : "<NOSRV>");
2394 break;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002395 default:
2396 src = "<NOSRV>";
2397 break;
2398 }
William Lallemand5f232402012-04-05 18:02:55 +02002399 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002400 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002401 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002402 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002403 last_isspace = 0;
2404 break;
2405
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002406 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002407 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002408 if (ret == NULL)
2409 goto out;
2410 tmplog = ret;
2411 last_isspace = 0;
2412 break;
2413
2414 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002415 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002416 if (ret == NULL)
2417 goto out;
2418 tmplog = ret;
2419 last_isspace = 0;
2420 break;
2421
2422 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002423 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002424 tmplog, dst + maxsize - tmplog);
2425 if (ret == NULL)
2426 goto out;
2427 tmplog = ret;
2428 last_isspace = 0;
2429 break;
2430
2431 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002432 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002433 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002434 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002435 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002436 last_isspace = 0;
2437 break;
2438
William Lallemand1d705562012-03-12 12:46:41 +01002439 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002440 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002441 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002442 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002443 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002444 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002445 last_isspace = 0;
2446 break;
2447
William Lallemand1d705562012-03-12 12:46:41 +01002448 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002449 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002450 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002451 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002452 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002453 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002454 last_isspace = 0;
2455 break;
2456
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002457 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002458 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002459 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002460 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002461 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002462 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002463 last_isspace = 0;
2464 break;
2465
Willy Tarreau27b639d2016-05-17 17:55:27 +02002466 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002467 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002468 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002469 tmplog, dst + maxsize - tmplog);
2470 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002471 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002472 tmplog, dst + maxsize - tmplog);
2473 if (ret == NULL)
2474 goto out;
2475 tmplog = ret;
2476 last_isspace = 0;
2477 break;
2478
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002479 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2480 if (!(fe->to_log & LW_BYTES))
2481 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002482 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 +02002483 tmplog, dst + maxsize - tmplog);
2484 if (ret == NULL)
2485 goto out;
2486 tmplog = ret;
2487 last_isspace = 0;
2488 break;
2489
2490 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002491 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002492 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002493 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002494 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002495 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002496 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002497 last_isspace = 0;
2498 break;
2499
Damien Claisse57c8eb92020-04-28 12:09:19 +00002500 case LOG_FMT_TU: // %Tu = total time seen by user = Tt - Ti
2501 if (!(fe->to_log & LW_BYTES))
2502 LOGCHAR('+');
2503 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0),
2504 tmplog, dst + maxsize - tmplog);
2505 if (ret == NULL)
2506 goto out;
2507 tmplog = ret;
2508 last_isspace = 0;
2509 break;
2510
Willy Tarreau2beef582012-12-20 17:22:52 +01002511 case LOG_FMT_STATUS: // %ST
Christopher Fauletce5e6bc2020-10-06 15:11:43 +02002512 ret = ltoa_o(status, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002513 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002514 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002515 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002516 last_isspace = 0;
2517 break;
2518
William Lallemand1d705562012-03-12 12:46:41 +01002519 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002520 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002521 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002522 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002523 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002524 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002525 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002526 last_isspace = 0;
2527 break;
2528
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002529 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002530 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002531 if (ret == NULL)
2532 goto out;
2533 tmplog = ret;
2534 last_isspace = 0;
2535 break;
2536
Willy Tarreau2beef582012-12-20 17:22:52 +01002537 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002538 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002539 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002540 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002541 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002542 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002543 last_isspace = 0;
2544 break;
2545
Willy Tarreau2beef582012-12-20 17:22:52 +01002546 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002547 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002548 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002549 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002550 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002551 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002552 last_isspace = 0;
2553 break;
2554
William Lallemand1d705562012-03-12 12:46:41 +01002555 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002556 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2557 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002558 *tmplog = '\0';
2559 last_isspace = 0;
2560 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002561
William Lallemand1d705562012-03-12 12:46:41 +01002562 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002563 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2564 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002565 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2566 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 +01002567 last_isspace = 0;
2568 break;
2569
William Lallemand1d705562012-03-12 12:46:41 +01002570 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002571 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002572 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002573 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002574 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002575 last_isspace = 0;
2576 break;
2577
William Lallemand1d705562012-03-12 12:46:41 +01002578 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002579 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002580 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002581 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002582 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002583 last_isspace = 0;
2584 break;
2585
William Lallemand1d705562012-03-12 12:46:41 +01002586 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002587 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002588 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002589 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002590 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002591 last_isspace = 0;
2592 break;
2593
William Lallemand1d705562012-03-12 12:46:41 +01002594 case LOG_FMT_SRVCONN: // %sc
Christopher Fauletfd818482021-04-14 14:01:41 +02002595 switch (obj_type(s ? s->target : sess->origin)) {
2596 case OBJ_TYPE_SERVER:
2597 ret = ultoa_o(__objt_server(s->target)->cur_sess,
2598 tmplog, dst + maxsize - tmplog);
2599 break;
2600 case OBJ_TYPE_CHECK:
2601 ret = ultoa_o(__objt_check(sess->origin)->server
2602 ? __objt_check(sess->origin)->server->cur_sess
2603 : 0, tmplog, dst + maxsize - tmplog);
2604 break;
2605 default:
2606 ret = ultoa_o(0, tmplog, dst + maxsize - tmplog);
2607 break;
2608 }
2609
William Lallemand1d705562012-03-12 12:46:41 +01002610 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002611 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002612 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002613 last_isspace = 0;
2614 break;
2615
William Lallemand1d705562012-03-12 12:46:41 +01002616 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002617 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002618 LOGCHAR('+');
Christopher Faulet02fc86e2021-12-23 13:32:42 +01002619 ret = ltoa_o(((s && cs_si(s->csb)->conn_retries > 0)
2620 ? (be->conn_retries - cs_si(s->csb)->conn_retries)
2621 : ((s && cs_si(s->csb)->state != SI_ST_INI) ? be->conn_retries : 0)),
Christopher Faulet1d26f222021-04-16 11:24:20 +02002622 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002623 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002624 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002625 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002626 last_isspace = 0;
2627 break;
2628
William Lallemand1d705562012-03-12 12:46:41 +01002629 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002630 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002631 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002632 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002633 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002634 last_isspace = 0;
2635 break;
2636
William Lallemand1d705562012-03-12 12:46:41 +01002637 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002638 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002639 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002640 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002641 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002642 last_isspace = 0;
2643 break;
2644
William Lallemand1d705562012-03-12 12:46:41 +01002645 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002646 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002647 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002648 if (tmp->options & LOG_OPT_QUOTE)
2649 LOGCHAR('"');
2650 LOGCHAR('{');
2651 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2652 if (hdr)
2653 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002654 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002655 ret = lf_encode_string(tmplog, dst + maxsize,
2656 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002657 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002658 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002659 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002660 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002661 }
2662 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002663 if (tmp->options & LOG_OPT_QUOTE)
2664 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002665 last_isspace = 0;
2666 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002667 break;
2668
William Lallemand1d705562012-03-12 12:46:41 +01002669 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002670 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002671 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002672 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2673 if (hdr > 0)
2674 LOGCHAR(' ');
2675 if (tmp->options & LOG_OPT_QUOTE)
2676 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002677 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002678 ret = lf_encode_string(tmplog, dst + maxsize,
2679 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002680 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002681 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002682 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002683 } else if (!(tmp->options & LOG_OPT_QUOTE))
2684 LOGCHAR('-');
2685 if (tmp->options & LOG_OPT_QUOTE)
2686 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002687 last_isspace = 0;
2688 }
2689 }
2690 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002691
William Lallemand1d705562012-03-12 12:46:41 +01002692
2693 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002694 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002695 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002696 if (tmp->options & LOG_OPT_QUOTE)
2697 LOGCHAR('"');
2698 LOGCHAR('{');
2699 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2700 if (hdr)
2701 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002702 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002703 ret = lf_encode_string(tmplog, dst + maxsize,
2704 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002705 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002706 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002707 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002708 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002709 }
2710 LOGCHAR('}');
2711 last_isspace = 0;
2712 if (tmp->options & LOG_OPT_QUOTE)
2713 LOGCHAR('"');
2714 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002715 break;
2716
William Lallemand1d705562012-03-12 12:46:41 +01002717 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002718 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002719 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002720 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2721 if (hdr > 0)
2722 LOGCHAR(' ');
2723 if (tmp->options & LOG_OPT_QUOTE)
2724 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002725 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002726 ret = lf_encode_string(tmplog, dst + maxsize,
2727 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002728 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002729 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002730 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002731 } else if (!(tmp->options & LOG_OPT_QUOTE))
2732 LOGCHAR('-');
2733 if (tmp->options & LOG_OPT_QUOTE)
2734 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002735 last_isspace = 0;
2736 }
2737 }
2738 break;
2739
William Lallemand1d705562012-03-12 12:46:41 +01002740 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002741 /* Request */
2742 if (tmp->options & LOG_OPT_QUOTE)
2743 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002744 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002745 ret = lf_encode_string(tmplog, dst + maxsize,
2746 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002747 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002748 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002749 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002750 if (tmp->options & LOG_OPT_QUOTE)
2751 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002752 last_isspace = 0;
2753 break;
William Lallemand5f232402012-04-05 18:02:55 +02002754
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002755 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002756 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002757
Willy Tarreaub7636d12015-06-17 19:58:02 +02002758 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002759 LOGCHAR('"');
2760
2761 end = uri + strlen(uri);
2762 // look for the first whitespace character
2763 while (uri < end && !HTTP_IS_SPHT(*uri))
2764 uri++;
2765
2766 // keep advancing past multiple spaces
2767 while (uri < end && HTTP_IS_SPHT(*uri)) {
2768 uri++; nspaces++;
2769 }
2770
2771 // look for first space or question mark after url
2772 spc = uri;
2773 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2774 spc++;
2775
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002776 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002777 chunk.area = "<BADREQ>";
2778 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002779 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002780 chunk.area = uri;
2781 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002782 }
2783
Dragan Dosen835b9212016-02-12 13:23:03 +01002784 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002785 if (ret == NULL || *ret != '\0')
2786 goto out;
2787
2788 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002789 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002790 LOGCHAR('"');
2791
2792 last_isspace = 0;
2793 break;
2794
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002795 case LOG_FMT_HTTP_PATH_ONLY: // %HPO
2796 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2797
2798 if (tmp->options & LOG_OPT_QUOTE)
2799 LOGCHAR('"');
2800
2801 end = uri + strlen(uri);
2802
2803 // look for the first whitespace character
2804 while (uri < end && !HTTP_IS_SPHT(*uri))
2805 uri++;
2806
2807 // keep advancing past multiple spaces
2808 while (uri < end && HTTP_IS_SPHT(*uri)) {
2809 uri++; nspaces++;
2810 }
2811
2812 // look for first space after url
2813 spc = uri;
2814 while (spc < end && !HTTP_IS_SPHT(*spc))
2815 spc++;
2816
Tim Duesterhus92c696e2021-02-28 16:11:36 +01002817 path = ist2(uri, spc - uri);
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002818
2819 // extract relative path without query params from url
Amaury Denoyellec453f952021-07-06 11:40:12 +02002820 parser = http_uri_parser_init(path);
2821 path = iststop(http_parse_path(&parser), '?');
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002822 if (!txn || !txn->uri || nspaces == 0) {
2823 chunk.area = "<BADREQ>";
2824 chunk.data = strlen("<BADREQ>");
2825 } else {
2826 chunk.area = path.ptr;
2827 chunk.data = path.len;
2828 }
2829
2830 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2831 if (ret == NULL || *ret != '\0')
2832 goto out;
2833
2834 tmplog = ret;
2835 if (tmp->options & LOG_OPT_QUOTE)
2836 LOGCHAR('"');
2837
2838 last_isspace = 0;
2839 break;
2840
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002841 case LOG_FMT_HTTP_QUERY: // %HQ
2842 if (tmp->options & LOG_OPT_QUOTE)
2843 LOGCHAR('"');
2844
Willy Tarreau57bc8912016-04-25 17:09:40 +02002845 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002846 chunk.area = "<BADREQ>";
2847 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002848 } else {
2849 uri = txn->uri;
2850 end = uri + strlen(uri);
2851 // look for the first question mark
2852 while (uri < end && *uri != '?')
2853 uri++;
2854
2855 qmark = uri;
2856 // look for first space or question mark after url
2857 while (uri < end && !HTTP_IS_SPHT(*uri))
2858 uri++;
2859
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002860 chunk.area = qmark;
2861 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002862 }
2863
Dragan Dosen835b9212016-02-12 13:23:03 +01002864 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002865 if (ret == NULL || *ret != '\0')
2866 goto out;
2867
2868 tmplog = ret;
2869 if (tmp->options & LOG_OPT_QUOTE)
2870 LOGCHAR('"');
2871
2872 last_isspace = 0;
2873 break;
2874
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002875 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002876 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002877
Willy Tarreaub7636d12015-06-17 19:58:02 +02002878 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002879 LOGCHAR('"');
2880
2881 end = uri + strlen(uri);
2882 // look for the first whitespace character
2883 while (uri < end && !HTTP_IS_SPHT(*uri))
2884 uri++;
2885
2886 // keep advancing past multiple spaces
2887 while (uri < end && HTTP_IS_SPHT(*uri)) {
2888 uri++; nspaces++;
2889 }
2890
2891 // look for first space after url
2892 spc = uri;
2893 while (spc < end && !HTTP_IS_SPHT(*spc))
2894 spc++;
2895
Willy Tarreau57bc8912016-04-25 17:09:40 +02002896 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002897 chunk.area = "<BADREQ>";
2898 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002899 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002900 chunk.area = uri;
2901 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002902 }
2903
Dragan Dosen835b9212016-02-12 13:23:03 +01002904 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002905 if (ret == NULL || *ret != '\0')
2906 goto out;
2907
2908 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002909 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002910 LOGCHAR('"');
2911
2912 last_isspace = 0;
2913 break;
2914
2915 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002916 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002917 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002918 LOGCHAR('"');
2919
2920 end = uri + strlen(uri);
2921 // look for the first whitespace character
2922 spc = uri;
2923 while (spc < end && !HTTP_IS_SPHT(*spc))
2924 spc++;
2925
2926 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002927 chunk.area = "<BADREQ>";
2928 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002929 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002930 chunk.area = uri;
2931 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002932 }
2933
Dragan Dosen835b9212016-02-12 13:23:03 +01002934 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002935 if (ret == NULL || *ret != '\0')
2936 goto out;
2937
2938 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002939 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002940 LOGCHAR('"');
2941
2942 last_isspace = 0;
2943 break;
2944
2945 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002946 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002947 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002948 LOGCHAR('"');
2949
2950 end = uri + strlen(uri);
2951 // look for the first whitespace character
2952 while (uri < end && !HTTP_IS_SPHT(*uri))
2953 uri++;
2954
2955 // keep advancing past multiple spaces
2956 while (uri < end && HTTP_IS_SPHT(*uri)) {
2957 uri++; nspaces++;
2958 }
2959
2960 // look for the next whitespace character
2961 while (uri < end && !HTTP_IS_SPHT(*uri))
2962 uri++;
2963
2964 // keep advancing past multiple spaces
2965 while (uri < end && HTTP_IS_SPHT(*uri))
2966 uri++;
2967
Willy Tarreau57bc8912016-04-25 17:09:40 +02002968 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002969 chunk.area = "<BADREQ>";
2970 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002971 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002972 chunk.area = "HTTP/0.9";
2973 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002974 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002975 chunk.area = uri;
2976 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002977 }
2978
Dragan Dosen835b9212016-02-12 13:23:03 +01002979 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002980 if (ret == NULL || *ret != '\0')
2981 goto out;
2982
2983 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002984 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002985 LOGCHAR('"');
2986
2987 last_isspace = 0;
2988 break;
2989
William Lallemand5f232402012-04-05 18:02:55 +02002990 case LOG_FMT_COUNTER: // %rt
2991 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002992 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002993 if (iret < 0 || iret > dst + maxsize - tmplog)
2994 goto out;
2995 last_isspace = 0;
2996 tmplog += iret;
2997 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002998 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002999 if (ret == NULL)
3000 goto out;
3001 tmplog = ret;
3002 last_isspace = 0;
3003 }
3004 break;
3005
Willy Tarreau7346acb2014-08-28 15:03:15 +02003006 case LOG_FMT_LOGCNT: // %lc
3007 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003008 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02003009 if (iret < 0 || iret > dst + maxsize - tmplog)
3010 goto out;
3011 last_isspace = 0;
3012 tmplog += iret;
3013 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003014 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02003015 if (ret == NULL)
3016 goto out;
3017 tmplog = ret;
3018 last_isspace = 0;
3019 }
3020 break;
3021
William Lallemand5f232402012-04-05 18:02:55 +02003022 case LOG_FMT_HOSTNAME: // %H
3023 src = hostname;
3024 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
3025 if (ret == NULL)
3026 goto out;
3027 tmplog = ret;
3028 last_isspace = 0;
3029 break;
3030
3031 case LOG_FMT_PID: // %pid
3032 if (tmp->options & LOG_OPT_HEXA) {
3033 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
3034 if (iret < 0 || iret > dst + maxsize - tmplog)
3035 goto out;
3036 last_isspace = 0;
3037 tmplog += iret;
3038 } else {
3039 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
3040 if (ret == NULL)
3041 goto out;
3042 tmplog = ret;
3043 last_isspace = 0;
3044 }
3045 break;
William Lallemanda73203e2012-03-12 12:48:57 +01003046
3047 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003048 ret = NULL;
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003049 if (s)
3050 ret = lf_text_len(tmplog, s->unique_id.ptr, s->unique_id.len, maxsize - (tmplog - dst), tmp);
3051 else
3052 ret = lf_text_len(tmplog, NULL, 0, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01003053 if (ret == NULL)
3054 goto out;
3055 tmplog = ret;
3056 last_isspace = 0;
3057 break;
3058
William Lallemandbddd4fd2012-02-27 11:23:10 +01003059 }
3060 }
3061
3062out:
William Lallemand1d705562012-03-12 12:46:41 +01003063 /* *tmplog is a unused character */
3064 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01003065 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003066
Willy Tarreaubaaee002006-06-26 02:48:02 +02003067}
3068
William Lallemand1d705562012-03-12 12:46:41 +01003069/*
Willy Tarreau87b09662015-04-03 00:22:06 +02003070 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01003071 * Will not log if the frontend has no log defined.
3072 */
Willy Tarreau87b09662015-04-03 00:22:06 +02003073void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01003074{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003075 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01003076 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003077 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01003078
3079 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02003080 err = (s->flags & SF_REDISP) ||
3081 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
3082 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Christopher Faulet02fc86e2021-12-23 13:32:42 +01003083 (cs_si(s->csb)->conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02003084 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02003085
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003086 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01003087 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003088
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003089 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01003090 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003091
Willy Tarreauabcd5142013-06-11 17:18:02 +02003092 if (s->logs.level) { /* loglevel was overridden */
3093 if (s->logs.level == -1) {
3094 s->logs.logwait = 0; /* logs disabled */
3095 return;
3096 }
3097 level = s->logs.level - 1;
3098 }
3099 else {
3100 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003101 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02003102 level = LOG_ERR;
3103 }
William Lallemand1d705562012-03-12 12:46:41 +01003104
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003105 /* if unique-id was not generated */
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003106 if (!isttest(s->unique_id) && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Tim Duesterhus2825b4b2020-02-28 15:13:34 +01003107 stream_generate_unique_id(s, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003108 }
3109
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003110 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3111 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
3112 &sess->fe->logformat_sd);
3113 }
3114
Dragan Dosen59cee972015-09-19 22:09:02 +02003115 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01003116 if (size > 0) {
Willy Tarreau4781b152021-04-06 13:53:36 +02003117 _HA_ATOMIC_INC(&sess->fe->log_count);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003118 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3119 logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01003120 s->logs.logwait = 0;
3121 }
3122}
William Lallemandbddd4fd2012-02-27 11:23:10 +01003123
Willy Tarreau53839352018-09-05 19:51:10 +02003124/*
3125 * send a minimalist log for the session. Will not log if the frontend has no
3126 * log defined. It is assumed that this is only used to report anomalies that
3127 * cannot lead to the creation of a regular stream. Because of this the log
3128 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
3129 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003130 * function to report unimportant events. It is safe to call this function with
3131 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02003132 */
3133void sess_log(struct session *sess)
3134{
3135 int size, level;
3136 int sd_size = 0;
3137
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003138 if (!sess)
3139 return;
3140
Willy Tarreau53839352018-09-05 19:51:10 +02003141 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3142 return;
3143
3144 level = LOG_INFO;
3145 if (sess->fe->options2 & PR_O2_LOGERRORS)
3146 level = LOG_ERR;
3147
3148 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3149 sd_size = sess_build_logline(sess, NULL,
3150 logline_rfc5424, global.max_syslog_len,
3151 &sess->fe->logformat_sd);
3152 }
3153
Remi Tricot-Le Bretonfe21fe72021-08-31 12:08:52 +02003154 if (!LIST_ISEMPTY(&sess->fe->logformat_error))
3155 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat_error);
3156 else
3157 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
Willy Tarreau53839352018-09-05 19:51:10 +02003158 if (size > 0) {
Willy Tarreau4781b152021-04-06 13:53:36 +02003159 _HA_ATOMIC_INC(&sess->fe->log_count);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003160 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3161 logline, size + 1, logline_rfc5424, sd_size);
Willy Tarreau53839352018-09-05 19:51:10 +02003162 }
3163}
3164
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003165void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3166{
3167 va_list argp;
3168 int data_len;
3169
3170 if (level < 0 || format == NULL || logline == NULL)
3171 return;
3172
3173 va_start(argp, format);
3174 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3175 if (data_len < 0 || data_len > global.max_syslog_len)
3176 data_len = global.max_syslog_len;
3177 va_end(argp);
3178
3179 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3180}
Emeric Brun54932b42020-07-07 09:43:24 +02003181/*
3182 * This function parse a received log message <buf>, of size <buflen>
3183 * it fills <level>, <facility> and <metadata> depending of the detected
3184 * header format and message will point on remaining payload of <size>
3185 *
3186 * <metadata> must point on a preallocated array of LOG_META_FIELDS*sizeof(struct ist)
3187 * struct ist len will be set to 0 if field is not found
3188 * <level> and <facility> will be set to -1 if not found.
3189 */
3190void parse_log_message(char *buf, size_t buflen, int *level, int *facility,
3191 struct ist *metadata, char **message, size_t *size)
3192{
3193
3194 char *p;
3195 int fac_level = 0;
3196
3197 *level = *facility = -1;
3198
3199 *message = buf;
3200 *size = buflen;
3201
3202 memset(metadata, 0, LOG_META_FIELDS*sizeof(struct ist));
3203
3204 p = buf;
3205 if (*size < 2 || *p != '<')
3206 return;
3207
3208 p++;
3209 while (*p != '>') {
3210 if (*p > '9' || *p < '0')
3211 return;
3212 fac_level = 10*fac_level + (*p - '0');
3213 p++;
3214 if ((p - buf) > buflen)
3215 return;
3216 }
3217
3218 *facility = fac_level >> 3;
3219 *level = fac_level & 0x7;
3220 p++;
3221
3222 metadata[LOG_META_PRIO] = ist2(buf, p - buf);
3223
3224 buflen -= p - buf;
3225 buf = p;
3226
3227 *size = buflen;
3228 *message = buf;
3229
3230 /* for rfc5424, prio is always followed by '1' and ' ' */
3231 if ((*size > 2) && (p[0] == '1') && (p[1] == ' ')) {
3232 /* format is always '1 TIMESTAMP HOSTNAME TAG PID MSGID STDATA '
3233 * followed by message.
3234 * Each header field can present NILVALUE: '-'
3235 */
3236
3237 p += 2;
3238 /* timestamp is NILVALUE '-' */
3239 if (*size > 2 && (p[0] == '-') && p[1] == ' ') {
3240 metadata[LOG_META_TIME] = ist2(p, 1);
3241 p++;
3242 }
3243 else if (*size > LOG_ISOTIME_MINLEN) {
3244 metadata[LOG_META_TIME].ptr = p;
3245
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05003246 /* check if optional secfrac is present
Emeric Brun54932b42020-07-07 09:43:24 +02003247 * in timestamp.
3248 * possible format are:
3249 * ex: '1970-01-01T00:00:00.000000Z'
3250 * '1970-01-01T00:00:00.000000+00:00'
3251 * '1970-01-01T00:00:00.000000-00:00'
3252 * '1970-01-01T00:00:00Z'
3253 * '1970-01-01T00:00:00+00:00'
3254 * '1970-01-01T00:00:00-00:00'
3255 */
3256 p += 19;
3257 if (*p == '.') {
3258 p++;
3259 if ((p - buf) >= buflen)
3260 goto bad_format;
3261 while (*p != 'Z' && *p != '+' && *p != '-') {
3262 if ((unsigned char)(*p - '0') > 9)
3263 goto bad_format;
3264
3265 p++;
3266 if ((p - buf) >= buflen)
3267 goto bad_format;
3268 }
3269 }
3270
3271 if (*p == 'Z')
3272 p++;
3273 else
3274 p += 6; /* case of '+00:00 or '-00:00' */
3275
3276 if ((p - buf) >= buflen || *p != ' ')
3277 goto bad_format;
3278 metadata[LOG_META_TIME].len = p - metadata[LOG_META_TIME].ptr;
3279 }
3280 else
3281 goto bad_format;
3282
3283
3284 p++;
3285 if ((p - buf) >= buflen || *p == ' ')
3286 goto bad_format;
3287
3288 metadata[LOG_META_HOST].ptr = p;
3289 while (*p != ' ') {
3290 p++;
3291 if ((p - buf) >= buflen)
3292 goto bad_format;
3293 }
3294 metadata[LOG_META_HOST].len = p - metadata[LOG_META_HOST].ptr;
3295 if (metadata[LOG_META_HOST].len == 1 && metadata[LOG_META_HOST].ptr[0] == '-')
3296 metadata[LOG_META_HOST].len = 0;
3297
3298 p++;
3299 if ((p - buf) >= buflen || *p == ' ')
3300 goto bad_format;
3301
3302 metadata[LOG_META_TAG].ptr = p;
3303 while (*p != ' ') {
3304 p++;
3305 if ((p - buf) >= buflen)
3306 goto bad_format;
3307 }
3308 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3309 if (metadata[LOG_META_TAG].len == 1 && metadata[LOG_META_TAG].ptr[0] == '-')
3310 metadata[LOG_META_TAG].len = 0;
3311
3312 p++;
3313 if ((p - buf) >= buflen || *p == ' ')
3314 goto bad_format;
3315
3316 metadata[LOG_META_PID].ptr = p;
3317 while (*p != ' ') {
3318 p++;
3319 if ((p - buf) >= buflen)
3320 goto bad_format;
3321 }
3322 metadata[LOG_META_PID].len = p - metadata[LOG_META_PID].ptr;
3323 if (metadata[LOG_META_PID].len == 1 && metadata[LOG_META_PID].ptr[0] == '-')
3324 metadata[LOG_META_PID].len = 0;
3325
3326 p++;
3327 if ((p - buf) >= buflen || *p == ' ')
3328 goto bad_format;
3329
3330 metadata[LOG_META_MSGID].ptr = p;
3331 while (*p != ' ') {
3332 p++;
3333 if ((p - buf) >= buflen)
3334 goto bad_format;
3335 }
3336 metadata[LOG_META_MSGID].len = p - metadata[LOG_META_MSGID].ptr;
3337 if (metadata[LOG_META_MSGID].len == 1 && metadata[LOG_META_MSGID].ptr[0] == '-')
3338 metadata[LOG_META_MSGID].len = 0;
3339
3340 p++;
3341 if ((p - buf) >= buflen || *p == ' ')
3342 goto bad_format;
3343
3344 /* structured data format is:
3345 * ex:
3346 * '[key1=value1 key2=value2][key3=value3]'
3347 *
3348 * space is invalid outside [] because
3349 * considered as the end of structured data field
3350 */
3351 metadata[LOG_META_STDATA].ptr = p;
3352 if (*p == '[') {
3353 int elem = 0;
3354
3355 while (1) {
3356 if (elem) {
3357 /* according to rfc this char is escaped in param values */
3358 if (*p == ']' && *(p-1) != '\\')
3359 elem = 0;
3360 }
3361 else {
3362 if (*p == '[')
3363 elem = 1;
3364 else if (*p == ' ')
3365 break;
3366 else
3367 goto bad_format;
3368 }
3369 p++;
3370 if ((p - buf) >= buflen)
3371 goto bad_format;
3372 }
3373 }
3374 else if (*p == '-') {
3375 /* case of NILVALUE */
3376 p++;
3377 if ((p - buf) >= buflen || *p != ' ')
3378 goto bad_format;
3379 }
3380 else
3381 goto bad_format;
3382
3383 metadata[LOG_META_STDATA].len = p - metadata[LOG_META_STDATA].ptr;
3384 if (metadata[LOG_META_STDATA].len == 1 && metadata[LOG_META_STDATA].ptr[0] == '-')
3385 metadata[LOG_META_STDATA].len = 0;
3386
3387 p++;
3388
3389 buflen -= p - buf;
3390 buf = p;
3391
3392 *size = buflen;
3393 *message = p;
3394 }
3395 else if (*size > LOG_LEGACYTIME_LEN) {
3396 int m;
3397
3398 /* supported header format according to rfc3164.
3399 * ex:
3400 * 'Jan 1 00:00:00 HOSTNAME TAG[PID]: '
3401 * or 'Jan 1 00:00:00 HOSTNAME TAG: '
3402 * or 'Jan 1 00:00:00 HOSTNAME '
3403 * Note: HOSTNAME is mandatory, and day
3404 * of month uses a single space prefix if
3405 * less than 10 to ensure hour offset is
3406 * always the same.
3407 */
3408
3409 /* Check month to see if it correspond to a rfc3164
3410 * header ex 'Jan 1 00:00:00' */
3411 for (m = 0; m < 12; m++)
3412 if (!memcmp(monthname[m], p, 3))
3413 break;
3414 /* Month not found */
3415 if (m == 12)
3416 goto bad_format;
3417
3418 metadata[LOG_META_TIME] = ist2(p, LOG_LEGACYTIME_LEN);
3419
3420 p += LOG_LEGACYTIME_LEN;
3421 if ((p - buf) >= buflen || *p != ' ')
3422 goto bad_format;
3423
3424 p++;
3425 if ((p - buf) >= buflen || *p == ' ')
3426 goto bad_format;
3427
3428 metadata[LOG_META_HOST].ptr = p;
3429 while (*p != ' ') {
3430 p++;
3431 if ((p - buf) >= buflen)
3432 goto bad_format;
3433 }
3434 metadata[LOG_META_HOST].len = p - metadata[LOG_META_HOST].ptr;
3435
3436 /* TAG seems to no be mandatory */
3437 p++;
3438
3439 buflen -= p - buf;
3440 buf = p;
3441
3442 *size = buflen;
3443 *message = buf;
3444
3445 if (!buflen)
3446 return;
3447
3448 while (((p - buf) < buflen) && *p != ' ' && *p != ':')
3449 p++;
3450
3451 /* a tag must present a trailing ':' */
3452 if (((p - buf) >= buflen) || *p != ':')
3453 return;
3454 p++;
3455 /* followed by a space */
3456 if (((p - buf) >= buflen) || *p != ' ')
3457 return;
3458
3459 /* rewind to parse tag and pid */
3460 p = buf;
3461 metadata[LOG_META_TAG].ptr = p;
3462 /* we have the guarantee that ':' will be reach before size limit */
3463 while (*p != ':') {
3464 if (*p == '[') {
3465 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3466 metadata[LOG_META_PID].ptr = p + 1;
3467 }
Tim Duesterhus7b5777d2021-03-02 18:57:28 +01003468 else if (*p == ']' && isttest(metadata[LOG_META_PID])) {
Emeric Brun54932b42020-07-07 09:43:24 +02003469 if (p[1] != ':')
3470 return;
3471 metadata[LOG_META_PID].len = p - metadata[LOG_META_PID].ptr;
3472 }
3473 p++;
3474 }
3475 if (!metadata[LOG_META_TAG].len)
3476 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3477
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05003478 /* let pass ':' and ' ', we still have warranty size is large enough */
Emeric Brun54932b42020-07-07 09:43:24 +02003479 p += 2;
3480
3481 buflen -= p - buf;
3482 buf = p;
3483
3484 *size = buflen;
3485 *message = buf;
3486 }
3487
3488 return;
3489
3490bad_format:
3491 /* bad syslog format, we reset all parsed syslog fields
3492 * but priority is kept because we are able to re-build
3493 * this message using LOF_FORMAT_PRIO.
3494 */
3495 metadata[LOG_META_TIME].len = 0;
3496 metadata[LOG_META_HOST].len = 0;
3497 metadata[LOG_META_TAG].len = 0;
3498 metadata[LOG_META_PID].len = 0;
3499 metadata[LOG_META_MSGID].len = 0;
3500 metadata[LOG_META_STDATA].len = 0;
3501
3502 return;
3503}
3504
3505/*
3506 * UDP syslog fd handler
3507 */
3508void syslog_fd_handler(int fd)
3509{
3510 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
3511 ssize_t ret = 0;
3512 struct buffer *buf = get_trash_chunk();
3513 size_t size;
3514 char *message;
3515 int level;
3516 int facility;
3517 struct listener *l = objt_listener(fdtab[fd].owner);
3518 int max_accept;
3519
Tim Duesterhus16554242021-09-15 13:58:49 +02003520 BUG_ON(!l);
Emeric Brun54932b42020-07-07 09:43:24 +02003521
Willy Tarreauf5090652021-04-06 17:23:40 +02003522 if (fdtab[fd].state & FD_POLL_IN) {
Emeric Brun54932b42020-07-07 09:43:24 +02003523
3524 if (!fd_recv_ready(fd))
3525 return;
3526
3527 max_accept = l->maxaccept ? l->maxaccept : 1;
3528
3529 do {
3530 /* Source address */
3531 struct sockaddr_storage saddr = {0};
3532 socklen_t saddrlen;
3533
3534 saddrlen = sizeof(saddr);
3535
3536 ret = recvfrom(fd, buf->area, buf->size, 0, (struct sockaddr *)&saddr, &saddrlen);
3537 if (ret < 0) {
3538 if (errno == EINTR)
3539 continue;
3540 if (errno == EAGAIN)
3541 fd_cant_recv(fd);
3542 goto out;
3543 }
3544 buf->data = ret;
3545
Emeric Brun45c457a2020-07-09 23:23:34 +02003546 /* update counters */
Willy Tarreau4781b152021-04-06 13:53:36 +02003547 _HA_ATOMIC_INC(&cum_log_messages);
Emeric Bruna39ecbd2020-10-05 14:33:12 +02003548 proxy_inc_fe_req_ctr(l, l->bind_conf->frontend);
Emeric Brun45c457a2020-07-09 23:23:34 +02003549
Emeric Brun54932b42020-07-07 09:43:24 +02003550 parse_log_message(buf->area, buf->data, &level, &facility, metadata, &message, &size);
3551
3552 process_send_log(&l->bind_conf->frontend->logsrvs, level, facility, metadata, message, size);
3553
3554 } while (--max_accept);
3555 }
3556
3557out:
3558 return;
3559}
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003560
Emeric Brun12941c82020-07-07 14:19:42 +02003561/*
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003562 * IO Handler to handle message exchange with a syslog tcp client
3563 */
3564static void syslog_io_handler(struct appctx *appctx)
3565{
3566 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
Christopher Faulet908628c2022-03-25 16:43:49 +01003567 struct conn_stream *cs = appctx->owner;
3568 struct stream *s = __cs_strm(cs);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003569 struct proxy *frontend = strm_fe(s);
3570 struct listener *l = strm_li(s);
3571 struct buffer *buf = get_trash_chunk();
3572 int max_accept;
3573 int to_skip;
3574 int facility;
3575 int level;
3576 char *message;
3577 size_t size;
3578
3579 max_accept = l->maxaccept ? l->maxaccept : 1;
Christopher Faulet908628c2022-03-25 16:43:49 +01003580 while (co_data(cs_oc(cs))) {
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003581 char c;
3582
3583 if (max_accept <= 0)
3584 goto missing_budget;
3585 max_accept--;
3586
Christopher Faulet908628c2022-03-25 16:43:49 +01003587 to_skip = co_getchar(cs_oc(cs), &c);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003588 if (!to_skip)
3589 goto missing_data;
3590 else if (to_skip < 0)
3591 goto cli_abort;
3592
3593 if (c == '<') {
3594 /* rfc-6587, Non-Transparent-Framing: messages separated by
3595 * a trailing LF or CR LF
3596 */
Christopher Faulet908628c2022-03-25 16:43:49 +01003597 to_skip = co_getline(cs_oc(cs), buf->area, buf->size);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003598 if (!to_skip)
3599 goto missing_data;
3600 else if (to_skip < 0)
3601 goto cli_abort;
3602
3603 if (buf->area[to_skip - 1] != '\n')
3604 goto parse_error;
3605
3606 buf->data = to_skip - 1;
3607
3608 /* according to rfc-6587, some devices adds CR before LF */
3609 if (buf->data && buf->area[buf->data - 1] == '\r')
3610 buf->data--;
3611
3612 }
3613 else if ((unsigned char)(c - '1') <= 8) {
3614 /* rfc-6587, Octet-Counting: message length in ASCII
3615 * (first digit can not be ZERO), followed by a space
3616 * and message length
3617 */
3618 char *p = NULL;
3619 int msglen;
3620
Christopher Faulet908628c2022-03-25 16:43:49 +01003621 to_skip = co_getword(cs_oc(cs), buf->area, buf->size, ' ');
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003622 if (!to_skip)
3623 goto missing_data;
3624 else if (to_skip < 0)
3625 goto cli_abort;
3626
3627 if (buf->area[to_skip - 1] != ' ')
3628 goto parse_error;
3629
3630 msglen = strtol(trash.area, &p, 10);
3631 if (!msglen || p != &buf->area[to_skip - 1])
3632 goto parse_error;
3633
3634 /* message seems too large */
3635 if (msglen > buf->size)
3636 goto parse_error;
3637
Christopher Faulet908628c2022-03-25 16:43:49 +01003638 msglen = co_getblk(cs_oc(cs), buf->area, msglen, to_skip);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003639 if (!msglen)
3640 goto missing_data;
3641 else if (msglen < 0)
3642 goto cli_abort;
3643
3644
3645 buf->data = msglen;
3646 to_skip += msglen;
3647 }
3648 else
3649 goto parse_error;
3650
Christopher Faulet908628c2022-03-25 16:43:49 +01003651 co_skip(cs_oc(cs), to_skip);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003652
3653 /* update counters */
Willy Tarreau4781b152021-04-06 13:53:36 +02003654 _HA_ATOMIC_INC(&cum_log_messages);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003655 proxy_inc_fe_req_ctr(l, frontend);
3656
3657 parse_log_message(buf->area, buf->data, &level, &facility, metadata, &message, &size);
3658
3659 process_send_log(&frontend->logsrvs, level, facility, metadata, message, size);
3660
3661 }
3662
3663missing_data:
3664 /* we need more data to read */
Christopher Faulet908628c2022-03-25 16:43:49 +01003665 cs_oc(cs)->flags |= CF_READ_DONTWAIT;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003666
3667 return;
3668
3669missing_budget:
3670 /* it may remain some stuff to do, let's retry later */
3671 appctx_wakeup(appctx);
3672
3673 return;
3674
3675parse_error:
3676 if (l->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02003677 _HA_ATOMIC_INC(&l->counters->failed_req);
3678 _HA_ATOMIC_INC(&frontend->fe_counters.failed_req);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003679
3680 goto close;
3681
3682cli_abort:
3683 if (l->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02003684 _HA_ATOMIC_INC(&l->counters->cli_aborts);
3685 _HA_ATOMIC_INC(&frontend->fe_counters.cli_aborts);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003686
3687close:
Christopher Faulet908628c2022-03-25 16:43:49 +01003688 si_shutw(cs->si);
3689 si_shutr(cs->si);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003690
Christopher Faulet908628c2022-03-25 16:43:49 +01003691 cs_ic(cs)->flags |= CF_READ_NULL;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003692
3693 return;
3694}
3695
3696static struct applet syslog_applet = {
3697 .obj_type = OBJ_TYPE_APPLET,
3698 .name = "<SYSLOG>", /* used for logging */
3699 .fct = syslog_io_handler,
3700 .release = NULL,
3701};
3702
3703/*
Emeric Brun12941c82020-07-07 14:19:42 +02003704 * Parse "log-forward" section and create corresponding sink buffer.
3705 *
3706 * The function returns 0 in success case, otherwise, it returns error
3707 * flags.
3708 */
3709int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3710{
3711 int err_code = 0;
3712 struct proxy *px;
3713 char *errmsg = NULL;
3714 const char *err = NULL;
3715
3716 if (strcmp(args[0], "log-forward") == 0) {
3717 if (!*args[1]) {
3718 ha_alert("parsing [%s:%d] : missing name for ip-forward section.\n", file, linenum);
3719 err_code |= ERR_ALERT | ERR_ABORT;
3720 goto out;
3721 }
3722
3723 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3724 goto out;
3725
3726 err = invalid_char(args[1]);
3727 if (err) {
3728 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3729 file, linenum, *err, args[0], args[1]);
3730 err_code |= ERR_ALERT | ERR_ABORT;
3731 goto out;
3732 }
3733
Emeric Brunb0c331f2020-10-07 17:05:59 +02003734 px = log_forward_by_name(args[1]);
3735 if (px) {
3736 ha_alert("Parsing [%s:%d]: log-forward section '%s' has the same name as another log-forward section declared at %s:%d.\n",
3737 file, linenum, args[1], px->conf.file, px->conf.line);
3738 err_code |= ERR_ALERT | ERR_FATAL;
3739 }
3740
3741 px = proxy_find_by_name(args[1], 0, 0);
3742 if (px) {
3743 ha_alert("Parsing [%s:%d]: log forward section '%s' has the same name as %s '%s' declared at %s:%d.\n",
3744 file, linenum, args[1], proxy_type_str(px),
3745 px->id, px->conf.file, px->conf.line);
3746 err_code |= ERR_ALERT | ERR_FATAL;
Emeric Brun12941c82020-07-07 14:19:42 +02003747 }
3748
3749 px = calloc(1, sizeof *px);
3750 if (!px) {
3751 err_code |= ERR_ALERT | ERR_FATAL;
3752 goto out;
3753 }
3754
Emeric Brun2ad2b1c2021-12-01 12:08:42 +01003755 init_new_proxy(px);
Emeric Brun12941c82020-07-07 14:19:42 +02003756 px->next = cfg_log_forward;
3757 cfg_log_forward = px;
Emeric Brun12941c82020-07-07 14:19:42 +02003758 px->conf.file = strdup(file);
3759 px->conf.line = linenum;
3760 px->mode = PR_MODE_SYSLOG;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003761 px->last_change = now.tv_sec;
3762 px->cap = PR_CAP_FE;
3763 px->maxconn = 10;
3764 px->timeout.client = TICK_ETERNITY;
3765 px->accept = frontend_accept;
3766 px->default_target = &syslog_applet.obj_type;
Emeric Brun12941c82020-07-07 14:19:42 +02003767 px->id = strdup(args[1]);
3768
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003769 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003770 else if (strcmp(args[0], "maxconn") == 0) { /* maxconn */
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003771 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], " Maybe you want 'fullconn' instead ?"))
3772 err_code |= ERR_WARN;
3773
3774 if (*(args[1]) == 0) {
3775 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
3776 err_code |= ERR_ALERT | ERR_FATAL;
3777 goto out;
3778 }
3779 cfg_log_forward->maxconn = atol(args[1]);
3780 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3781 goto out;
3782 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003783 else if (strcmp(args[0], "backlog") == 0) { /* backlog */
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003784 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], NULL))
3785 err_code |= ERR_WARN;
3786
3787 if (*(args[1]) == 0) {
3788 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
3789 err_code |= ERR_ALERT | ERR_FATAL;
3790 goto out;
3791 }
3792 cfg_log_forward->backlog = atol(args[1]);
3793 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3794 goto out;
3795 }
3796 else if (strcmp(args[0], "bind") == 0) {
3797 int cur_arg;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003798 struct bind_conf *bind_conf;
3799 struct bind_kw *kw;
3800 struct listener *l;
3801
3802 cur_arg = 1;
3803
3804 bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum,
3805 NULL, xprt_get(XPRT_RAW));
3806 if (!bind_conf) {
3807 ha_alert("parsing [%s:%d] : out of memory error.", file, linenum);
3808 err_code |= ERR_ALERT | ERR_FATAL;
3809 goto out;
3810 }
3811
3812 if (!str2listener(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) {
3813 if (errmsg && *errmsg) {
3814 indent_msg(&errmsg, 2);
3815 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3816 }
3817 else {
3818 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3819 file, linenum, args[0], args[1], args[2]);
3820 err_code |= ERR_ALERT | ERR_FATAL;
3821 goto out;
3822 }
3823 }
3824 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
Willy Tarreau66161322021-02-19 15:50:27 +01003825 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003826 l->accept = session_accept_fd;
3827 l->analysers |= cfg_log_forward->fe_req_ana;
3828 l->default_target = cfg_log_forward->default_target;
3829 global.maxsock++;
3830 }
3831 cur_arg++;
3832
3833 while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) {
3834 int ret;
3835
3836 ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg);
3837 err_code |= ret;
3838 if (ret) {
3839 if (errmsg && *errmsg) {
3840 indent_msg(&errmsg, 2);
3841 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
3842 }
3843 else
3844 ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n",
3845 file, linenum, args[cur_arg]);
3846 if (ret & ERR_FATAL)
3847 goto out;
3848 }
3849 cur_arg += 1 + kw->skip;
3850 }
3851 if (*args[cur_arg] != 0) {
Willy Tarreau433b05f2021-03-12 10:14:07 +01003852 const char *best = bind_find_best_kw(args[cur_arg]);
3853 if (best)
3854 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
3855 file, linenum, args[cur_arg], cursection, best);
3856 else
3857 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
3858 file, linenum, args[cur_arg], cursection);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003859 err_code |= ERR_ALERT | ERR_FATAL;
3860 goto out;
3861 }
Emeric Brun12941c82020-07-07 14:19:42 +02003862 }
Willy Tarreau76aaa7f2020-09-16 15:07:22 +02003863 else if (strcmp(args[0], "dgram-bind") == 0) {
Emeric Brun12941c82020-07-07 14:19:42 +02003864 int cur_arg;
Emeric Brun12941c82020-07-07 14:19:42 +02003865 struct bind_conf *bind_conf;
3866 struct bind_kw *kw;
3867 struct listener *l;
3868
3869 cur_arg = 1;
3870
3871 bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum,
3872 NULL, xprt_get(XPRT_RAW));
Christopher Faulet0c6d1dc2021-04-12 16:56:37 +02003873 if (!bind_conf) {
3874 ha_alert("parsing [%s:%d] : out of memory error.", file, linenum);
3875 err_code |= ERR_ALERT | ERR_FATAL;
3876 goto out;
3877 }
Emeric Brun12941c82020-07-07 14:19:42 +02003878
Willy Tarreau26ff5da2020-09-16 15:22:19 +02003879 if (!str2receiver(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) {
Emeric Brun12941c82020-07-07 14:19:42 +02003880 if (errmsg && *errmsg) {
3881 indent_msg(&errmsg, 2);
3882 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3883 }
3884 else {
3885 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3886 file, linenum, args[0], args[1], args[2]);
Emeric Brun12941c82020-07-07 14:19:42 +02003887 }
Willy Tarreau3b139e52020-09-16 16:24:14 +02003888 err_code |= ERR_ALERT | ERR_FATAL;
3889 goto out;
Emeric Brun12941c82020-07-07 14:19:42 +02003890 }
3891 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
Willy Tarreau26ff5da2020-09-16 15:22:19 +02003892 /* the fact that the sockets are of type dgram is guaranteed by str2receiver() */
Willy Tarreau66161322021-02-19 15:50:27 +01003893 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
Willy Tarreaue140a692020-10-15 21:25:32 +02003894 l->rx.iocb = syslog_fd_handler;
Emeric Brun12941c82020-07-07 14:19:42 +02003895 global.maxsock++;
3896 }
3897 cur_arg++;
3898
3899 while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) {
3900 int ret;
3901
3902 ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg);
3903 err_code |= ret;
3904 if (ret) {
3905 if (errmsg && *errmsg) {
3906 indent_msg(&errmsg, 2);
3907 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
3908 }
3909 else
3910 ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n",
3911 file, linenum, args[cur_arg]);
3912 if (ret & ERR_FATAL)
3913 goto out;
3914 }
3915 cur_arg += 1 + kw->skip;
3916 }
3917 if (*args[cur_arg] != 0) {
Willy Tarreau433b05f2021-03-12 10:14:07 +01003918 const char *best = bind_find_best_kw(args[cur_arg]);
3919 if (best)
3920 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
3921 file, linenum, args[cur_arg], cursection, best);
3922 else
3923 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
3924 file, linenum, args[cur_arg], cursection);
Emeric Brun12941c82020-07-07 14:19:42 +02003925 err_code |= ERR_ALERT | ERR_FATAL;
3926 goto out;
3927 }
3928 }
3929 else if (strcmp(args[0], "log") == 0) {
Emeric Brun9533a702021-04-02 10:13:43 +02003930 if (!parse_logsrv(args, &cfg_log_forward->logsrvs, (kwm == KWM_NO), file, linenum, &errmsg)) {
Emeric Brun12941c82020-07-07 14:19:42 +02003931 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3932 err_code |= ERR_ALERT | ERR_FATAL;
3933 goto out;
3934 }
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003935 }
3936 else if (strcmp(args[0], "timeout") == 0) {
3937 const char *res;
3938 unsigned timeout;
3939
3940 if (strcmp(args[1], "client") != 0) {
3941 ha_alert("parsing [%s:%d] : unknown keyword '%s %s' in log-forward section.\n", file, linenum, args[0], args[1]);
3942 err_code |= ERR_ALERT | ERR_FATAL;
3943 goto out;
3944 }
3945
3946 if (*args[2] == 0) {
3947 ha_alert("parsing [%s:%d] : missing timeout client value.\n", file, linenum);
3948 err_code |= ERR_ALERT | ERR_FATAL;
3949 goto out;
3950 }
3951 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3952 if (res == PARSE_TIME_OVER) {
3953 memprintf(&errmsg, "timer overflow in argument '%s' to 'timeout client' (maximum value is 2147483647 ms or ~24.8 days)", args[2]);
3954 }
3955 else if (res == PARSE_TIME_UNDER) {
3956 memprintf(&errmsg, "timer underflow in argument '%s' to 'timeout client' (minimum non-null value is 1 ms)", args[2]);
3957 }
3958 else if (res) {
3959 memprintf(&errmsg, "unexpected character '%c' in 'timeout client'", *res);
3960 return -1;
3961 }
3962
3963 if (res) {
3964 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3965 err_code |= ERR_ALERT | ERR_FATAL;
3966 goto out;
3967 }
3968 cfg_log_forward->timeout.client = MS_TO_TICKS(timeout);
Emeric Brun12941c82020-07-07 14:19:42 +02003969 }
Willy Tarreauf9feec22020-09-16 15:04:33 +02003970 else {
3971 ha_alert("parsing [%s:%d] : unknown keyword '%s' in log-forward section.\n", file, linenum, args[0]);
3972 err_code |= ERR_ALERT | ERR_ABORT;
3973 goto out;
3974 }
Emeric Brun12941c82020-07-07 14:19:42 +02003975out:
3976 return err_code;
3977}
3978
Willy Tarreau0108d902018-11-25 19:14:37 +01003979
Emeric Brun12941c82020-07-07 14:19:42 +02003980/* config parsers for this section */
3981REGISTER_CONFIG_SECTION("log-forward", cfg_parse_log_forward, NULL);
3982
Willy Tarreau082b6282019-05-22 14:42:12 +02003983REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3984REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003985
Willy Tarreaubaaee002006-06-26 02:48:02 +02003986/*
3987 * Local variables:
3988 * c-indent-level: 8
3989 * c-basic-offset: 8
3990 * End:
3991 */