blob: 5897d2ff4f849128779d06925f318a43c2506b5e [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 Faulet8da67aa2022-03-29 17:53:09 +02002119 addr = (s ? cs_src(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 Faulet8da67aa2022-03-29 17:53:09 +02002132 addr = (s ? cs_src(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 Faulet8da67aa2022-03-29 17:53:09 +02002150 addr = (s ? cs_dst(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 Faulet8da67aa2022-03-29 17:53:09 +02002163 addr = (s ? cs_dst(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 Faulet731c8e62022-03-29 16:08:44 +02002619 ret = ltoa_o((s ? s->conn_retries : 0), tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002620 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002621 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002622 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002623 last_isspace = 0;
2624 break;
2625
William Lallemand1d705562012-03-12 12:46:41 +01002626 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002627 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002628 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002629 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002630 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002631 last_isspace = 0;
2632 break;
2633
William Lallemand1d705562012-03-12 12:46:41 +01002634 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002635 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002636 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002637 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002638 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002639 last_isspace = 0;
2640 break;
2641
William Lallemand1d705562012-03-12 12:46:41 +01002642 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002643 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002644 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002645 if (tmp->options & LOG_OPT_QUOTE)
2646 LOGCHAR('"');
2647 LOGCHAR('{');
2648 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2649 if (hdr)
2650 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002651 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002652 ret = lf_encode_string(tmplog, dst + maxsize,
2653 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002654 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002655 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002656 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002657 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002658 }
2659 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002660 if (tmp->options & LOG_OPT_QUOTE)
2661 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002662 last_isspace = 0;
2663 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002664 break;
2665
William Lallemand1d705562012-03-12 12:46:41 +01002666 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002667 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002668 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002669 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2670 if (hdr > 0)
2671 LOGCHAR(' ');
2672 if (tmp->options & LOG_OPT_QUOTE)
2673 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002674 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002675 ret = lf_encode_string(tmplog, dst + maxsize,
2676 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002677 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002678 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002679 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002680 } else if (!(tmp->options & LOG_OPT_QUOTE))
2681 LOGCHAR('-');
2682 if (tmp->options & LOG_OPT_QUOTE)
2683 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002684 last_isspace = 0;
2685 }
2686 }
2687 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002688
William Lallemand1d705562012-03-12 12:46:41 +01002689
2690 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002691 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002692 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002693 if (tmp->options & LOG_OPT_QUOTE)
2694 LOGCHAR('"');
2695 LOGCHAR('{');
2696 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2697 if (hdr)
2698 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002699 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002700 ret = lf_encode_string(tmplog, dst + maxsize,
2701 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002702 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002703 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002704 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002705 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002706 }
2707 LOGCHAR('}');
2708 last_isspace = 0;
2709 if (tmp->options & LOG_OPT_QUOTE)
2710 LOGCHAR('"');
2711 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002712 break;
2713
William Lallemand1d705562012-03-12 12:46:41 +01002714 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002715 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002716 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002717 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2718 if (hdr > 0)
2719 LOGCHAR(' ');
2720 if (tmp->options & LOG_OPT_QUOTE)
2721 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002722 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002723 ret = lf_encode_string(tmplog, dst + maxsize,
2724 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002725 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002726 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002727 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002728 } else if (!(tmp->options & LOG_OPT_QUOTE))
2729 LOGCHAR('-');
2730 if (tmp->options & LOG_OPT_QUOTE)
2731 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002732 last_isspace = 0;
2733 }
2734 }
2735 break;
2736
William Lallemand1d705562012-03-12 12:46:41 +01002737 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002738 /* Request */
2739 if (tmp->options & LOG_OPT_QUOTE)
2740 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002741 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002742 ret = lf_encode_string(tmplog, dst + maxsize,
2743 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002744 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002745 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002746 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002747 if (tmp->options & LOG_OPT_QUOTE)
2748 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002749 last_isspace = 0;
2750 break;
William Lallemand5f232402012-04-05 18:02:55 +02002751
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002752 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002753 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002754
Willy Tarreaub7636d12015-06-17 19:58:02 +02002755 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002756 LOGCHAR('"');
2757
2758 end = uri + strlen(uri);
2759 // look for the first whitespace character
2760 while (uri < end && !HTTP_IS_SPHT(*uri))
2761 uri++;
2762
2763 // keep advancing past multiple spaces
2764 while (uri < end && HTTP_IS_SPHT(*uri)) {
2765 uri++; nspaces++;
2766 }
2767
2768 // look for first space or question mark after url
2769 spc = uri;
2770 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2771 spc++;
2772
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002773 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002774 chunk.area = "<BADREQ>";
2775 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002776 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002777 chunk.area = uri;
2778 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002779 }
2780
Dragan Dosen835b9212016-02-12 13:23:03 +01002781 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002782 if (ret == NULL || *ret != '\0')
2783 goto out;
2784
2785 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002786 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002787 LOGCHAR('"');
2788
2789 last_isspace = 0;
2790 break;
2791
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002792 case LOG_FMT_HTTP_PATH_ONLY: // %HPO
2793 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
2794
2795 if (tmp->options & LOG_OPT_QUOTE)
2796 LOGCHAR('"');
2797
2798 end = uri + strlen(uri);
2799
2800 // look for the first whitespace character
2801 while (uri < end && !HTTP_IS_SPHT(*uri))
2802 uri++;
2803
2804 // keep advancing past multiple spaces
2805 while (uri < end && HTTP_IS_SPHT(*uri)) {
2806 uri++; nspaces++;
2807 }
2808
2809 // look for first space after url
2810 spc = uri;
2811 while (spc < end && !HTTP_IS_SPHT(*spc))
2812 spc++;
2813
Tim Duesterhus92c696e2021-02-28 16:11:36 +01002814 path = ist2(uri, spc - uri);
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002815
2816 // extract relative path without query params from url
Amaury Denoyellec453f952021-07-06 11:40:12 +02002817 parser = http_uri_parser_init(path);
2818 path = iststop(http_parse_path(&parser), '?');
Maciej Zdebfcdfd852020-11-30 18:27:47 +00002819 if (!txn || !txn->uri || nspaces == 0) {
2820 chunk.area = "<BADREQ>";
2821 chunk.data = strlen("<BADREQ>");
2822 } else {
2823 chunk.area = path.ptr;
2824 chunk.data = path.len;
2825 }
2826
2827 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
2828 if (ret == NULL || *ret != '\0')
2829 goto out;
2830
2831 tmplog = ret;
2832 if (tmp->options & LOG_OPT_QUOTE)
2833 LOGCHAR('"');
2834
2835 last_isspace = 0;
2836 break;
2837
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002838 case LOG_FMT_HTTP_QUERY: // %HQ
2839 if (tmp->options & LOG_OPT_QUOTE)
2840 LOGCHAR('"');
2841
Willy Tarreau57bc8912016-04-25 17:09:40 +02002842 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002843 chunk.area = "<BADREQ>";
2844 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002845 } else {
2846 uri = txn->uri;
2847 end = uri + strlen(uri);
2848 // look for the first question mark
2849 while (uri < end && *uri != '?')
2850 uri++;
2851
2852 qmark = uri;
2853 // look for first space or question mark after url
2854 while (uri < end && !HTTP_IS_SPHT(*uri))
2855 uri++;
2856
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002857 chunk.area = qmark;
2858 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002859 }
2860
Dragan Dosen835b9212016-02-12 13:23:03 +01002861 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002862 if (ret == NULL || *ret != '\0')
2863 goto out;
2864
2865 tmplog = ret;
2866 if (tmp->options & LOG_OPT_QUOTE)
2867 LOGCHAR('"');
2868
2869 last_isspace = 0;
2870 break;
2871
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002872 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002873 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002874
Willy Tarreaub7636d12015-06-17 19:58:02 +02002875 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002876 LOGCHAR('"');
2877
2878 end = uri + strlen(uri);
2879 // look for the first whitespace character
2880 while (uri < end && !HTTP_IS_SPHT(*uri))
2881 uri++;
2882
2883 // keep advancing past multiple spaces
2884 while (uri < end && HTTP_IS_SPHT(*uri)) {
2885 uri++; nspaces++;
2886 }
2887
2888 // look for first space after url
2889 spc = uri;
2890 while (spc < end && !HTTP_IS_SPHT(*spc))
2891 spc++;
2892
Willy Tarreau57bc8912016-04-25 17:09:40 +02002893 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002894 chunk.area = "<BADREQ>";
2895 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002896 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002897 chunk.area = uri;
2898 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002899 }
2900
Dragan Dosen835b9212016-02-12 13:23:03 +01002901 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002902 if (ret == NULL || *ret != '\0')
2903 goto out;
2904
2905 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002906 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002907 LOGCHAR('"');
2908
2909 last_isspace = 0;
2910 break;
2911
2912 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002913 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002914 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002915 LOGCHAR('"');
2916
2917 end = uri + strlen(uri);
2918 // look for the first whitespace character
2919 spc = uri;
2920 while (spc < end && !HTTP_IS_SPHT(*spc))
2921 spc++;
2922
2923 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002924 chunk.area = "<BADREQ>";
2925 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002926 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002927 chunk.area = uri;
2928 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002929 }
2930
Dragan Dosen835b9212016-02-12 13:23:03 +01002931 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002932 if (ret == NULL || *ret != '\0')
2933 goto out;
2934
2935 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002936 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002937 LOGCHAR('"');
2938
2939 last_isspace = 0;
2940 break;
2941
2942 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002943 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002944 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002945 LOGCHAR('"');
2946
2947 end = uri + strlen(uri);
2948 // look for the first whitespace character
2949 while (uri < end && !HTTP_IS_SPHT(*uri))
2950 uri++;
2951
2952 // keep advancing past multiple spaces
2953 while (uri < end && HTTP_IS_SPHT(*uri)) {
2954 uri++; nspaces++;
2955 }
2956
2957 // look for the next whitespace character
2958 while (uri < end && !HTTP_IS_SPHT(*uri))
2959 uri++;
2960
2961 // keep advancing past multiple spaces
2962 while (uri < end && HTTP_IS_SPHT(*uri))
2963 uri++;
2964
Willy Tarreau57bc8912016-04-25 17:09:40 +02002965 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002966 chunk.area = "<BADREQ>";
2967 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002968 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002969 chunk.area = "HTTP/0.9";
2970 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002971 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002972 chunk.area = uri;
2973 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002974 }
2975
Dragan Dosen835b9212016-02-12 13:23:03 +01002976 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002977 if (ret == NULL || *ret != '\0')
2978 goto out;
2979
2980 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002981 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002982 LOGCHAR('"');
2983
2984 last_isspace = 0;
2985 break;
2986
William Lallemand5f232402012-04-05 18:02:55 +02002987 case LOG_FMT_COUNTER: // %rt
2988 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002989 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002990 if (iret < 0 || iret > dst + maxsize - tmplog)
2991 goto out;
2992 last_isspace = 0;
2993 tmplog += iret;
2994 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002995 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002996 if (ret == NULL)
2997 goto out;
2998 tmplog = ret;
2999 last_isspace = 0;
3000 }
3001 break;
3002
Willy Tarreau7346acb2014-08-28 15:03:15 +02003003 case LOG_FMT_LOGCNT: // %lc
3004 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003005 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02003006 if (iret < 0 || iret > dst + maxsize - tmplog)
3007 goto out;
3008 last_isspace = 0;
3009 tmplog += iret;
3010 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003011 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02003012 if (ret == NULL)
3013 goto out;
3014 tmplog = ret;
3015 last_isspace = 0;
3016 }
3017 break;
3018
William Lallemand5f232402012-04-05 18:02:55 +02003019 case LOG_FMT_HOSTNAME: // %H
3020 src = hostname;
3021 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
3022 if (ret == NULL)
3023 goto out;
3024 tmplog = ret;
3025 last_isspace = 0;
3026 break;
3027
3028 case LOG_FMT_PID: // %pid
3029 if (tmp->options & LOG_OPT_HEXA) {
3030 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
3031 if (iret < 0 || iret > dst + maxsize - tmplog)
3032 goto out;
3033 last_isspace = 0;
3034 tmplog += iret;
3035 } else {
3036 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
3037 if (ret == NULL)
3038 goto out;
3039 tmplog = ret;
3040 last_isspace = 0;
3041 }
3042 break;
William Lallemanda73203e2012-03-12 12:48:57 +01003043
3044 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003045 ret = NULL;
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003046 if (s)
3047 ret = lf_text_len(tmplog, s->unique_id.ptr, s->unique_id.len, maxsize - (tmplog - dst), tmp);
3048 else
3049 ret = lf_text_len(tmplog, NULL, 0, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01003050 if (ret == NULL)
3051 goto out;
3052 tmplog = ret;
3053 last_isspace = 0;
3054 break;
3055
William Lallemandbddd4fd2012-02-27 11:23:10 +01003056 }
3057 }
3058
3059out:
William Lallemand1d705562012-03-12 12:46:41 +01003060 /* *tmplog is a unused character */
3061 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01003062 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003063
Willy Tarreaubaaee002006-06-26 02:48:02 +02003064}
3065
William Lallemand1d705562012-03-12 12:46:41 +01003066/*
Willy Tarreau87b09662015-04-03 00:22:06 +02003067 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01003068 * Will not log if the frontend has no log defined.
3069 */
Willy Tarreau87b09662015-04-03 00:22:06 +02003070void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01003071{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003072 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01003073 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003074 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01003075
3076 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02003077 err = (s->flags & SF_REDISP) ||
3078 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
3079 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Christopher Faulet909f3182022-03-29 15:42:09 +02003080 (s->conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02003081 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02003082
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003083 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01003084 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003085
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003086 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01003087 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003088
Willy Tarreauabcd5142013-06-11 17:18:02 +02003089 if (s->logs.level) { /* loglevel was overridden */
3090 if (s->logs.level == -1) {
3091 s->logs.logwait = 0; /* logs disabled */
3092 return;
3093 }
3094 level = s->logs.level - 1;
3095 }
3096 else {
3097 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003098 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02003099 level = LOG_ERR;
3100 }
William Lallemand1d705562012-03-12 12:46:41 +01003101
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003102 /* if unique-id was not generated */
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003103 if (!isttest(s->unique_id) && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Tim Duesterhus2825b4b2020-02-28 15:13:34 +01003104 stream_generate_unique_id(s, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003105 }
3106
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003107 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3108 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
3109 &sess->fe->logformat_sd);
3110 }
3111
Dragan Dosen59cee972015-09-19 22:09:02 +02003112 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01003113 if (size > 0) {
Willy Tarreau4781b152021-04-06 13:53:36 +02003114 _HA_ATOMIC_INC(&sess->fe->log_count);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003115 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3116 logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01003117 s->logs.logwait = 0;
3118 }
3119}
William Lallemandbddd4fd2012-02-27 11:23:10 +01003120
Willy Tarreau53839352018-09-05 19:51:10 +02003121/*
3122 * send a minimalist log for the session. Will not log if the frontend has no
3123 * log defined. It is assumed that this is only used to report anomalies that
3124 * cannot lead to the creation of a regular stream. Because of this the log
3125 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
3126 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003127 * function to report unimportant events. It is safe to call this function with
3128 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02003129 */
3130void sess_log(struct session *sess)
3131{
3132 int size, level;
3133 int sd_size = 0;
3134
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003135 if (!sess)
3136 return;
3137
Willy Tarreau53839352018-09-05 19:51:10 +02003138 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3139 return;
3140
3141 level = LOG_INFO;
3142 if (sess->fe->options2 & PR_O2_LOGERRORS)
3143 level = LOG_ERR;
3144
3145 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3146 sd_size = sess_build_logline(sess, NULL,
3147 logline_rfc5424, global.max_syslog_len,
3148 &sess->fe->logformat_sd);
3149 }
3150
Remi Tricot-Le Bretonfe21fe72021-08-31 12:08:52 +02003151 if (!LIST_ISEMPTY(&sess->fe->logformat_error))
3152 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat_error);
3153 else
3154 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
Willy Tarreau53839352018-09-05 19:51:10 +02003155 if (size > 0) {
Willy Tarreau4781b152021-04-06 13:53:36 +02003156 _HA_ATOMIC_INC(&sess->fe->log_count);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003157 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3158 logline, size + 1, logline_rfc5424, sd_size);
Willy Tarreau53839352018-09-05 19:51:10 +02003159 }
3160}
3161
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003162void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3163{
3164 va_list argp;
3165 int data_len;
3166
3167 if (level < 0 || format == NULL || logline == NULL)
3168 return;
3169
3170 va_start(argp, format);
3171 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3172 if (data_len < 0 || data_len > global.max_syslog_len)
3173 data_len = global.max_syslog_len;
3174 va_end(argp);
3175
3176 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3177}
Emeric Brun54932b42020-07-07 09:43:24 +02003178/*
3179 * This function parse a received log message <buf>, of size <buflen>
3180 * it fills <level>, <facility> and <metadata> depending of the detected
3181 * header format and message will point on remaining payload of <size>
3182 *
3183 * <metadata> must point on a preallocated array of LOG_META_FIELDS*sizeof(struct ist)
3184 * struct ist len will be set to 0 if field is not found
3185 * <level> and <facility> will be set to -1 if not found.
3186 */
3187void parse_log_message(char *buf, size_t buflen, int *level, int *facility,
3188 struct ist *metadata, char **message, size_t *size)
3189{
3190
3191 char *p;
3192 int fac_level = 0;
3193
3194 *level = *facility = -1;
3195
3196 *message = buf;
3197 *size = buflen;
3198
3199 memset(metadata, 0, LOG_META_FIELDS*sizeof(struct ist));
3200
3201 p = buf;
3202 if (*size < 2 || *p != '<')
3203 return;
3204
3205 p++;
3206 while (*p != '>') {
3207 if (*p > '9' || *p < '0')
3208 return;
3209 fac_level = 10*fac_level + (*p - '0');
3210 p++;
3211 if ((p - buf) > buflen)
3212 return;
3213 }
3214
3215 *facility = fac_level >> 3;
3216 *level = fac_level & 0x7;
3217 p++;
3218
3219 metadata[LOG_META_PRIO] = ist2(buf, p - buf);
3220
3221 buflen -= p - buf;
3222 buf = p;
3223
3224 *size = buflen;
3225 *message = buf;
3226
3227 /* for rfc5424, prio is always followed by '1' and ' ' */
3228 if ((*size > 2) && (p[0] == '1') && (p[1] == ' ')) {
3229 /* format is always '1 TIMESTAMP HOSTNAME TAG PID MSGID STDATA '
3230 * followed by message.
3231 * Each header field can present NILVALUE: '-'
3232 */
3233
3234 p += 2;
3235 /* timestamp is NILVALUE '-' */
3236 if (*size > 2 && (p[0] == '-') && p[1] == ' ') {
3237 metadata[LOG_META_TIME] = ist2(p, 1);
3238 p++;
3239 }
3240 else if (*size > LOG_ISOTIME_MINLEN) {
3241 metadata[LOG_META_TIME].ptr = p;
3242
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05003243 /* check if optional secfrac is present
Emeric Brun54932b42020-07-07 09:43:24 +02003244 * in timestamp.
3245 * possible format are:
3246 * ex: '1970-01-01T00:00:00.000000Z'
3247 * '1970-01-01T00:00:00.000000+00:00'
3248 * '1970-01-01T00:00:00.000000-00:00'
3249 * '1970-01-01T00:00:00Z'
3250 * '1970-01-01T00:00:00+00:00'
3251 * '1970-01-01T00:00:00-00:00'
3252 */
3253 p += 19;
3254 if (*p == '.') {
3255 p++;
3256 if ((p - buf) >= buflen)
3257 goto bad_format;
3258 while (*p != 'Z' && *p != '+' && *p != '-') {
3259 if ((unsigned char)(*p - '0') > 9)
3260 goto bad_format;
3261
3262 p++;
3263 if ((p - buf) >= buflen)
3264 goto bad_format;
3265 }
3266 }
3267
3268 if (*p == 'Z')
3269 p++;
3270 else
3271 p += 6; /* case of '+00:00 or '-00:00' */
3272
3273 if ((p - buf) >= buflen || *p != ' ')
3274 goto bad_format;
3275 metadata[LOG_META_TIME].len = p - metadata[LOG_META_TIME].ptr;
3276 }
3277 else
3278 goto bad_format;
3279
3280
3281 p++;
3282 if ((p - buf) >= buflen || *p == ' ')
3283 goto bad_format;
3284
3285 metadata[LOG_META_HOST].ptr = p;
3286 while (*p != ' ') {
3287 p++;
3288 if ((p - buf) >= buflen)
3289 goto bad_format;
3290 }
3291 metadata[LOG_META_HOST].len = p - metadata[LOG_META_HOST].ptr;
3292 if (metadata[LOG_META_HOST].len == 1 && metadata[LOG_META_HOST].ptr[0] == '-')
3293 metadata[LOG_META_HOST].len = 0;
3294
3295 p++;
3296 if ((p - buf) >= buflen || *p == ' ')
3297 goto bad_format;
3298
3299 metadata[LOG_META_TAG].ptr = p;
3300 while (*p != ' ') {
3301 p++;
3302 if ((p - buf) >= buflen)
3303 goto bad_format;
3304 }
3305 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3306 if (metadata[LOG_META_TAG].len == 1 && metadata[LOG_META_TAG].ptr[0] == '-')
3307 metadata[LOG_META_TAG].len = 0;
3308
3309 p++;
3310 if ((p - buf) >= buflen || *p == ' ')
3311 goto bad_format;
3312
3313 metadata[LOG_META_PID].ptr = p;
3314 while (*p != ' ') {
3315 p++;
3316 if ((p - buf) >= buflen)
3317 goto bad_format;
3318 }
3319 metadata[LOG_META_PID].len = p - metadata[LOG_META_PID].ptr;
3320 if (metadata[LOG_META_PID].len == 1 && metadata[LOG_META_PID].ptr[0] == '-')
3321 metadata[LOG_META_PID].len = 0;
3322
3323 p++;
3324 if ((p - buf) >= buflen || *p == ' ')
3325 goto bad_format;
3326
3327 metadata[LOG_META_MSGID].ptr = p;
3328 while (*p != ' ') {
3329 p++;
3330 if ((p - buf) >= buflen)
3331 goto bad_format;
3332 }
3333 metadata[LOG_META_MSGID].len = p - metadata[LOG_META_MSGID].ptr;
3334 if (metadata[LOG_META_MSGID].len == 1 && metadata[LOG_META_MSGID].ptr[0] == '-')
3335 metadata[LOG_META_MSGID].len = 0;
3336
3337 p++;
3338 if ((p - buf) >= buflen || *p == ' ')
3339 goto bad_format;
3340
3341 /* structured data format is:
3342 * ex:
3343 * '[key1=value1 key2=value2][key3=value3]'
3344 *
3345 * space is invalid outside [] because
3346 * considered as the end of structured data field
3347 */
3348 metadata[LOG_META_STDATA].ptr = p;
3349 if (*p == '[') {
3350 int elem = 0;
3351
3352 while (1) {
3353 if (elem) {
3354 /* according to rfc this char is escaped in param values */
3355 if (*p == ']' && *(p-1) != '\\')
3356 elem = 0;
3357 }
3358 else {
3359 if (*p == '[')
3360 elem = 1;
3361 else if (*p == ' ')
3362 break;
3363 else
3364 goto bad_format;
3365 }
3366 p++;
3367 if ((p - buf) >= buflen)
3368 goto bad_format;
3369 }
3370 }
3371 else if (*p == '-') {
3372 /* case of NILVALUE */
3373 p++;
3374 if ((p - buf) >= buflen || *p != ' ')
3375 goto bad_format;
3376 }
3377 else
3378 goto bad_format;
3379
3380 metadata[LOG_META_STDATA].len = p - metadata[LOG_META_STDATA].ptr;
3381 if (metadata[LOG_META_STDATA].len == 1 && metadata[LOG_META_STDATA].ptr[0] == '-')
3382 metadata[LOG_META_STDATA].len = 0;
3383
3384 p++;
3385
3386 buflen -= p - buf;
3387 buf = p;
3388
3389 *size = buflen;
3390 *message = p;
3391 }
3392 else if (*size > LOG_LEGACYTIME_LEN) {
3393 int m;
3394
3395 /* supported header format according to rfc3164.
3396 * ex:
3397 * 'Jan 1 00:00:00 HOSTNAME TAG[PID]: '
3398 * or 'Jan 1 00:00:00 HOSTNAME TAG: '
3399 * or 'Jan 1 00:00:00 HOSTNAME '
3400 * Note: HOSTNAME is mandatory, and day
3401 * of month uses a single space prefix if
3402 * less than 10 to ensure hour offset is
3403 * always the same.
3404 */
3405
3406 /* Check month to see if it correspond to a rfc3164
3407 * header ex 'Jan 1 00:00:00' */
3408 for (m = 0; m < 12; m++)
3409 if (!memcmp(monthname[m], p, 3))
3410 break;
3411 /* Month not found */
3412 if (m == 12)
3413 goto bad_format;
3414
3415 metadata[LOG_META_TIME] = ist2(p, LOG_LEGACYTIME_LEN);
3416
3417 p += LOG_LEGACYTIME_LEN;
3418 if ((p - buf) >= buflen || *p != ' ')
3419 goto bad_format;
3420
3421 p++;
3422 if ((p - buf) >= buflen || *p == ' ')
3423 goto bad_format;
3424
3425 metadata[LOG_META_HOST].ptr = p;
3426 while (*p != ' ') {
3427 p++;
3428 if ((p - buf) >= buflen)
3429 goto bad_format;
3430 }
3431 metadata[LOG_META_HOST].len = p - metadata[LOG_META_HOST].ptr;
3432
3433 /* TAG seems to no be mandatory */
3434 p++;
3435
3436 buflen -= p - buf;
3437 buf = p;
3438
3439 *size = buflen;
3440 *message = buf;
3441
3442 if (!buflen)
3443 return;
3444
3445 while (((p - buf) < buflen) && *p != ' ' && *p != ':')
3446 p++;
3447
3448 /* a tag must present a trailing ':' */
3449 if (((p - buf) >= buflen) || *p != ':')
3450 return;
3451 p++;
3452 /* followed by a space */
3453 if (((p - buf) >= buflen) || *p != ' ')
3454 return;
3455
3456 /* rewind to parse tag and pid */
3457 p = buf;
3458 metadata[LOG_META_TAG].ptr = p;
3459 /* we have the guarantee that ':' will be reach before size limit */
3460 while (*p != ':') {
3461 if (*p == '[') {
3462 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3463 metadata[LOG_META_PID].ptr = p + 1;
3464 }
Tim Duesterhus7b5777d2021-03-02 18:57:28 +01003465 else if (*p == ']' && isttest(metadata[LOG_META_PID])) {
Emeric Brun54932b42020-07-07 09:43:24 +02003466 if (p[1] != ':')
3467 return;
3468 metadata[LOG_META_PID].len = p - metadata[LOG_META_PID].ptr;
3469 }
3470 p++;
3471 }
3472 if (!metadata[LOG_META_TAG].len)
3473 metadata[LOG_META_TAG].len = p - metadata[LOG_META_TAG].ptr;
3474
Ilya Shipitsin6b79f382020-07-23 00:32:55 +05003475 /* let pass ':' and ' ', we still have warranty size is large enough */
Emeric Brun54932b42020-07-07 09:43:24 +02003476 p += 2;
3477
3478 buflen -= p - buf;
3479 buf = p;
3480
3481 *size = buflen;
3482 *message = buf;
3483 }
3484
3485 return;
3486
3487bad_format:
3488 /* bad syslog format, we reset all parsed syslog fields
3489 * but priority is kept because we are able to re-build
3490 * this message using LOF_FORMAT_PRIO.
3491 */
3492 metadata[LOG_META_TIME].len = 0;
3493 metadata[LOG_META_HOST].len = 0;
3494 metadata[LOG_META_TAG].len = 0;
3495 metadata[LOG_META_PID].len = 0;
3496 metadata[LOG_META_MSGID].len = 0;
3497 metadata[LOG_META_STDATA].len = 0;
3498
3499 return;
3500}
3501
3502/*
3503 * UDP syslog fd handler
3504 */
3505void syslog_fd_handler(int fd)
3506{
3507 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
3508 ssize_t ret = 0;
3509 struct buffer *buf = get_trash_chunk();
3510 size_t size;
3511 char *message;
3512 int level;
3513 int facility;
3514 struct listener *l = objt_listener(fdtab[fd].owner);
3515 int max_accept;
3516
Tim Duesterhus16554242021-09-15 13:58:49 +02003517 BUG_ON(!l);
Emeric Brun54932b42020-07-07 09:43:24 +02003518
Willy Tarreauf5090652021-04-06 17:23:40 +02003519 if (fdtab[fd].state & FD_POLL_IN) {
Emeric Brun54932b42020-07-07 09:43:24 +02003520
3521 if (!fd_recv_ready(fd))
3522 return;
3523
3524 max_accept = l->maxaccept ? l->maxaccept : 1;
3525
3526 do {
3527 /* Source address */
3528 struct sockaddr_storage saddr = {0};
3529 socklen_t saddrlen;
3530
3531 saddrlen = sizeof(saddr);
3532
3533 ret = recvfrom(fd, buf->area, buf->size, 0, (struct sockaddr *)&saddr, &saddrlen);
3534 if (ret < 0) {
3535 if (errno == EINTR)
3536 continue;
3537 if (errno == EAGAIN)
3538 fd_cant_recv(fd);
3539 goto out;
3540 }
3541 buf->data = ret;
3542
Emeric Brun45c457a2020-07-09 23:23:34 +02003543 /* update counters */
Willy Tarreau4781b152021-04-06 13:53:36 +02003544 _HA_ATOMIC_INC(&cum_log_messages);
Emeric Bruna39ecbd2020-10-05 14:33:12 +02003545 proxy_inc_fe_req_ctr(l, l->bind_conf->frontend);
Emeric Brun45c457a2020-07-09 23:23:34 +02003546
Emeric Brun54932b42020-07-07 09:43:24 +02003547 parse_log_message(buf->area, buf->data, &level, &facility, metadata, &message, &size);
3548
3549 process_send_log(&l->bind_conf->frontend->logsrvs, level, facility, metadata, message, size);
3550
3551 } while (--max_accept);
3552 }
3553
3554out:
3555 return;
3556}
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003557
Emeric Brun12941c82020-07-07 14:19:42 +02003558/*
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003559 * IO Handler to handle message exchange with a syslog tcp client
3560 */
3561static void syslog_io_handler(struct appctx *appctx)
3562{
3563 static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
Christopher Faulet908628c2022-03-25 16:43:49 +01003564 struct conn_stream *cs = appctx->owner;
3565 struct stream *s = __cs_strm(cs);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003566 struct proxy *frontend = strm_fe(s);
3567 struct listener *l = strm_li(s);
3568 struct buffer *buf = get_trash_chunk();
3569 int max_accept;
3570 int to_skip;
3571 int facility;
3572 int level;
3573 char *message;
3574 size_t size;
3575
3576 max_accept = l->maxaccept ? l->maxaccept : 1;
Christopher Faulet908628c2022-03-25 16:43:49 +01003577 while (co_data(cs_oc(cs))) {
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003578 char c;
3579
3580 if (max_accept <= 0)
3581 goto missing_budget;
3582 max_accept--;
3583
Christopher Faulet908628c2022-03-25 16:43:49 +01003584 to_skip = co_getchar(cs_oc(cs), &c);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003585 if (!to_skip)
3586 goto missing_data;
3587 else if (to_skip < 0)
3588 goto cli_abort;
3589
3590 if (c == '<') {
3591 /* rfc-6587, Non-Transparent-Framing: messages separated by
3592 * a trailing LF or CR LF
3593 */
Christopher Faulet908628c2022-03-25 16:43:49 +01003594 to_skip = co_getline(cs_oc(cs), buf->area, buf->size);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003595 if (!to_skip)
3596 goto missing_data;
3597 else if (to_skip < 0)
3598 goto cli_abort;
3599
3600 if (buf->area[to_skip - 1] != '\n')
3601 goto parse_error;
3602
3603 buf->data = to_skip - 1;
3604
3605 /* according to rfc-6587, some devices adds CR before LF */
3606 if (buf->data && buf->area[buf->data - 1] == '\r')
3607 buf->data--;
3608
3609 }
3610 else if ((unsigned char)(c - '1') <= 8) {
3611 /* rfc-6587, Octet-Counting: message length in ASCII
3612 * (first digit can not be ZERO), followed by a space
3613 * and message length
3614 */
3615 char *p = NULL;
3616 int msglen;
3617
Christopher Faulet908628c2022-03-25 16:43:49 +01003618 to_skip = co_getword(cs_oc(cs), buf->area, buf->size, ' ');
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003619 if (!to_skip)
3620 goto missing_data;
3621 else if (to_skip < 0)
3622 goto cli_abort;
3623
3624 if (buf->area[to_skip - 1] != ' ')
3625 goto parse_error;
3626
3627 msglen = strtol(trash.area, &p, 10);
3628 if (!msglen || p != &buf->area[to_skip - 1])
3629 goto parse_error;
3630
3631 /* message seems too large */
3632 if (msglen > buf->size)
3633 goto parse_error;
3634
Christopher Faulet908628c2022-03-25 16:43:49 +01003635 msglen = co_getblk(cs_oc(cs), buf->area, msglen, to_skip);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003636 if (!msglen)
3637 goto missing_data;
3638 else if (msglen < 0)
3639 goto cli_abort;
3640
3641
3642 buf->data = msglen;
3643 to_skip += msglen;
3644 }
3645 else
3646 goto parse_error;
3647
Christopher Faulet908628c2022-03-25 16:43:49 +01003648 co_skip(cs_oc(cs), to_skip);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003649
3650 /* update counters */
Willy Tarreau4781b152021-04-06 13:53:36 +02003651 _HA_ATOMIC_INC(&cum_log_messages);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003652 proxy_inc_fe_req_ctr(l, frontend);
3653
3654 parse_log_message(buf->area, buf->data, &level, &facility, metadata, &message, &size);
3655
3656 process_send_log(&frontend->logsrvs, level, facility, metadata, message, size);
3657
3658 }
3659
3660missing_data:
3661 /* we need more data to read */
Christopher Faulet908628c2022-03-25 16:43:49 +01003662 cs_oc(cs)->flags |= CF_READ_DONTWAIT;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003663
3664 return;
3665
3666missing_budget:
3667 /* it may remain some stuff to do, let's retry later */
3668 appctx_wakeup(appctx);
3669
3670 return;
3671
3672parse_error:
3673 if (l->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02003674 _HA_ATOMIC_INC(&l->counters->failed_req);
3675 _HA_ATOMIC_INC(&frontend->fe_counters.failed_req);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003676
3677 goto close;
3678
3679cli_abort:
3680 if (l->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02003681 _HA_ATOMIC_INC(&l->counters->cli_aborts);
3682 _HA_ATOMIC_INC(&frontend->fe_counters.cli_aborts);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003683
3684close:
Christopher Fauletda098e62022-03-31 17:44:45 +02003685 cs_shutw(cs);
3686 cs_shutr(cs);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003687
Christopher Faulet908628c2022-03-25 16:43:49 +01003688 cs_ic(cs)->flags |= CF_READ_NULL;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003689
3690 return;
3691}
3692
3693static struct applet syslog_applet = {
3694 .obj_type = OBJ_TYPE_APPLET,
3695 .name = "<SYSLOG>", /* used for logging */
3696 .fct = syslog_io_handler,
3697 .release = NULL,
3698};
3699
3700/*
Emeric Brun12941c82020-07-07 14:19:42 +02003701 * Parse "log-forward" section and create corresponding sink buffer.
3702 *
3703 * The function returns 0 in success case, otherwise, it returns error
3704 * flags.
3705 */
3706int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm)
3707{
3708 int err_code = 0;
3709 struct proxy *px;
3710 char *errmsg = NULL;
3711 const char *err = NULL;
3712
3713 if (strcmp(args[0], "log-forward") == 0) {
3714 if (!*args[1]) {
3715 ha_alert("parsing [%s:%d] : missing name for ip-forward section.\n", file, linenum);
3716 err_code |= ERR_ALERT | ERR_ABORT;
3717 goto out;
3718 }
3719
3720 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3721 goto out;
3722
3723 err = invalid_char(args[1]);
3724 if (err) {
3725 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3726 file, linenum, *err, args[0], args[1]);
3727 err_code |= ERR_ALERT | ERR_ABORT;
3728 goto out;
3729 }
3730
Emeric Brunb0c331f2020-10-07 17:05:59 +02003731 px = log_forward_by_name(args[1]);
3732 if (px) {
3733 ha_alert("Parsing [%s:%d]: log-forward section '%s' has the same name as another log-forward section declared at %s:%d.\n",
3734 file, linenum, args[1], px->conf.file, px->conf.line);
3735 err_code |= ERR_ALERT | ERR_FATAL;
3736 }
3737
3738 px = proxy_find_by_name(args[1], 0, 0);
3739 if (px) {
3740 ha_alert("Parsing [%s:%d]: log forward section '%s' has the same name as %s '%s' declared at %s:%d.\n",
3741 file, linenum, args[1], proxy_type_str(px),
3742 px->id, px->conf.file, px->conf.line);
3743 err_code |= ERR_ALERT | ERR_FATAL;
Emeric Brun12941c82020-07-07 14:19:42 +02003744 }
3745
3746 px = calloc(1, sizeof *px);
3747 if (!px) {
3748 err_code |= ERR_ALERT | ERR_FATAL;
3749 goto out;
3750 }
3751
Emeric Brun2ad2b1c2021-12-01 12:08:42 +01003752 init_new_proxy(px);
Emeric Brun12941c82020-07-07 14:19:42 +02003753 px->next = cfg_log_forward;
3754 cfg_log_forward = px;
Emeric Brun12941c82020-07-07 14:19:42 +02003755 px->conf.file = strdup(file);
3756 px->conf.line = linenum;
3757 px->mode = PR_MODE_SYSLOG;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003758 px->last_change = now.tv_sec;
3759 px->cap = PR_CAP_FE;
3760 px->maxconn = 10;
3761 px->timeout.client = TICK_ETERNITY;
3762 px->accept = frontend_accept;
3763 px->default_target = &syslog_applet.obj_type;
Emeric Brun12941c82020-07-07 14:19:42 +02003764 px->id = strdup(args[1]);
3765
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003766 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003767 else if (strcmp(args[0], "maxconn") == 0) { /* maxconn */
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003768 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], " Maybe you want 'fullconn' instead ?"))
3769 err_code |= ERR_WARN;
3770
3771 if (*(args[1]) == 0) {
3772 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
3773 err_code |= ERR_ALERT | ERR_FATAL;
3774 goto out;
3775 }
3776 cfg_log_forward->maxconn = atol(args[1]);
3777 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3778 goto out;
3779 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003780 else if (strcmp(args[0], "backlog") == 0) { /* backlog */
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003781 if (warnifnotcap(cfg_log_forward, PR_CAP_FE, file, linenum, args[0], NULL))
3782 err_code |= ERR_WARN;
3783
3784 if (*(args[1]) == 0) {
3785 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
3786 err_code |= ERR_ALERT | ERR_FATAL;
3787 goto out;
3788 }
3789 cfg_log_forward->backlog = atol(args[1]);
3790 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3791 goto out;
3792 }
3793 else if (strcmp(args[0], "bind") == 0) {
3794 int cur_arg;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003795 struct bind_conf *bind_conf;
3796 struct bind_kw *kw;
3797 struct listener *l;
3798
3799 cur_arg = 1;
3800
3801 bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum,
3802 NULL, xprt_get(XPRT_RAW));
3803 if (!bind_conf) {
3804 ha_alert("parsing [%s:%d] : out of memory error.", file, linenum);
3805 err_code |= ERR_ALERT | ERR_FATAL;
3806 goto out;
3807 }
3808
3809 if (!str2listener(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) {
3810 if (errmsg && *errmsg) {
3811 indent_msg(&errmsg, 2);
3812 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3813 }
3814 else {
3815 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3816 file, linenum, args[0], args[1], args[2]);
3817 err_code |= ERR_ALERT | ERR_FATAL;
3818 goto out;
3819 }
3820 }
3821 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
Willy Tarreau66161322021-02-19 15:50:27 +01003822 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003823 l->accept = session_accept_fd;
3824 l->analysers |= cfg_log_forward->fe_req_ana;
3825 l->default_target = cfg_log_forward->default_target;
3826 global.maxsock++;
3827 }
3828 cur_arg++;
3829
3830 while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) {
3831 int ret;
3832
3833 ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg);
3834 err_code |= ret;
3835 if (ret) {
3836 if (errmsg && *errmsg) {
3837 indent_msg(&errmsg, 2);
3838 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
3839 }
3840 else
3841 ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n",
3842 file, linenum, args[cur_arg]);
3843 if (ret & ERR_FATAL)
3844 goto out;
3845 }
3846 cur_arg += 1 + kw->skip;
3847 }
3848 if (*args[cur_arg] != 0) {
Willy Tarreau433b05f2021-03-12 10:14:07 +01003849 const char *best = bind_find_best_kw(args[cur_arg]);
3850 if (best)
3851 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
3852 file, linenum, args[cur_arg], cursection, best);
3853 else
3854 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
3855 file, linenum, args[cur_arg], cursection);
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003856 err_code |= ERR_ALERT | ERR_FATAL;
3857 goto out;
3858 }
Emeric Brun12941c82020-07-07 14:19:42 +02003859 }
Willy Tarreau76aaa7f2020-09-16 15:07:22 +02003860 else if (strcmp(args[0], "dgram-bind") == 0) {
Emeric Brun12941c82020-07-07 14:19:42 +02003861 int cur_arg;
Emeric Brun12941c82020-07-07 14:19:42 +02003862 struct bind_conf *bind_conf;
3863 struct bind_kw *kw;
3864 struct listener *l;
3865
3866 cur_arg = 1;
3867
3868 bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum,
3869 NULL, xprt_get(XPRT_RAW));
Christopher Faulet0c6d1dc2021-04-12 16:56:37 +02003870 if (!bind_conf) {
3871 ha_alert("parsing [%s:%d] : out of memory error.", file, linenum);
3872 err_code |= ERR_ALERT | ERR_FATAL;
3873 goto out;
3874 }
Emeric Brun12941c82020-07-07 14:19:42 +02003875
Willy Tarreau26ff5da2020-09-16 15:22:19 +02003876 if (!str2receiver(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) {
Emeric Brun12941c82020-07-07 14:19:42 +02003877 if (errmsg && *errmsg) {
3878 indent_msg(&errmsg, 2);
3879 ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
3880 }
3881 else {
3882 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
3883 file, linenum, args[0], args[1], args[2]);
Emeric Brun12941c82020-07-07 14:19:42 +02003884 }
Willy Tarreau3b139e52020-09-16 16:24:14 +02003885 err_code |= ERR_ALERT | ERR_FATAL;
3886 goto out;
Emeric Brun12941c82020-07-07 14:19:42 +02003887 }
3888 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
Willy Tarreau26ff5da2020-09-16 15:22:19 +02003889 /* the fact that the sockets are of type dgram is guaranteed by str2receiver() */
Willy Tarreau66161322021-02-19 15:50:27 +01003890 l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : MAX_ACCEPT;
Willy Tarreaue140a692020-10-15 21:25:32 +02003891 l->rx.iocb = syslog_fd_handler;
Emeric Brun12941c82020-07-07 14:19:42 +02003892 global.maxsock++;
3893 }
3894 cur_arg++;
3895
3896 while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) {
3897 int ret;
3898
3899 ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg);
3900 err_code |= ret;
3901 if (ret) {
3902 if (errmsg && *errmsg) {
3903 indent_msg(&errmsg, 2);
3904 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
3905 }
3906 else
3907 ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n",
3908 file, linenum, args[cur_arg]);
3909 if (ret & ERR_FATAL)
3910 goto out;
3911 }
3912 cur_arg += 1 + kw->skip;
3913 }
3914 if (*args[cur_arg] != 0) {
Willy Tarreau433b05f2021-03-12 10:14:07 +01003915 const char *best = bind_find_best_kw(args[cur_arg]);
3916 if (best)
3917 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
3918 file, linenum, args[cur_arg], cursection, best);
3919 else
3920 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
3921 file, linenum, args[cur_arg], cursection);
Emeric Brun12941c82020-07-07 14:19:42 +02003922 err_code |= ERR_ALERT | ERR_FATAL;
3923 goto out;
3924 }
3925 }
3926 else if (strcmp(args[0], "log") == 0) {
Emeric Brun9533a702021-04-02 10:13:43 +02003927 if (!parse_logsrv(args, &cfg_log_forward->logsrvs, (kwm == KWM_NO), file, linenum, &errmsg)) {
Emeric Brun12941c82020-07-07 14:19:42 +02003928 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3929 err_code |= ERR_ALERT | ERR_FATAL;
3930 goto out;
3931 }
Emeric Bruncbb7bf72020-10-05 14:39:35 +02003932 }
3933 else if (strcmp(args[0], "timeout") == 0) {
3934 const char *res;
3935 unsigned timeout;
3936
3937 if (strcmp(args[1], "client") != 0) {
3938 ha_alert("parsing [%s:%d] : unknown keyword '%s %s' in log-forward section.\n", file, linenum, args[0], args[1]);
3939 err_code |= ERR_ALERT | ERR_FATAL;
3940 goto out;
3941 }
3942
3943 if (*args[2] == 0) {
3944 ha_alert("parsing [%s:%d] : missing timeout client value.\n", file, linenum);
3945 err_code |= ERR_ALERT | ERR_FATAL;
3946 goto out;
3947 }
3948 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3949 if (res == PARSE_TIME_OVER) {
3950 memprintf(&errmsg, "timer overflow in argument '%s' to 'timeout client' (maximum value is 2147483647 ms or ~24.8 days)", args[2]);
3951 }
3952 else if (res == PARSE_TIME_UNDER) {
3953 memprintf(&errmsg, "timer underflow in argument '%s' to 'timeout client' (minimum non-null value is 1 ms)", args[2]);
3954 }
3955 else if (res) {
3956 memprintf(&errmsg, "unexpected character '%c' in 'timeout client'", *res);
3957 return -1;
3958 }
3959
3960 if (res) {
3961 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3962 err_code |= ERR_ALERT | ERR_FATAL;
3963 goto out;
3964 }
3965 cfg_log_forward->timeout.client = MS_TO_TICKS(timeout);
Emeric Brun12941c82020-07-07 14:19:42 +02003966 }
Willy Tarreauf9feec22020-09-16 15:04:33 +02003967 else {
3968 ha_alert("parsing [%s:%d] : unknown keyword '%s' in log-forward section.\n", file, linenum, args[0]);
3969 err_code |= ERR_ALERT | ERR_ABORT;
3970 goto out;
3971 }
Emeric Brun12941c82020-07-07 14:19:42 +02003972out:
3973 return err_code;
3974}
3975
Willy Tarreau0108d902018-11-25 19:14:37 +01003976
Emeric Brun12941c82020-07-07 14:19:42 +02003977/* config parsers for this section */
3978REGISTER_CONFIG_SECTION("log-forward", cfg_parse_log_forward, NULL);
3979
Willy Tarreau082b6282019-05-22 14:42:12 +02003980REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3981REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003982
Willy Tarreaubaaee002006-06-26 02:48:02 +02003983/*
3984 * Local variables:
3985 * c-indent-level: 8
3986 * c-basic-offset: 8
3987 * End:
3988 */