blob: 1b34c637e23687c6920dbd183c3603f42a2886a9 [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 Tarreaue3ba5f02006-06-29 18:54:54 +020027#include <common/config.h>
Willy Tarreaud6d06902009-08-19 11:22:33 +020028#include <common/compat.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010029#include <common/initcall.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020030#include <common/standard.h>
Willy Tarreaufb278672006-10-15 15:38:50 +020031#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020032
Christopher Fauletc1b730a2017-10-24 12:00:51 +020033#include <types/cli.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020034#include <types/global.h>
William Lallemand723b73a2012-02-08 16:37:49 +010035#include <types/log.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020036
Christopher Fauletc1b730a2017-10-24 12:00:51 +020037#include <proto/applet.h>
38#include <proto/cli.h>
Willy Tarreaud52a7f82019-08-30 14:05:35 +020039#include <proto/fd.h>
William Lallemand5f232402012-04-05 18:02:55 +020040#include <proto/frontend.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020041#include <proto/log.h>
Willy Tarreauc8368452012-12-21 00:09:23 +010042#include <proto/sample.h>
Willy Tarreauc125cef2019-05-10 09:58:43 +020043#include <proto/ssl_sock.h>
Willy Tarreaufb0afa72015-04-03 14:46:27 +020044#include <proto/stream.h>
Willy Tarreau827aee92011-03-10 16:55:02 +010045#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020046
Dragan Dosen43885c72015-10-01 13:18:13 +020047struct log_fmt {
48 char *name;
49 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020050 struct buffer sep1; /* first pid separator */
51 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020052 } pid;
53};
54
55static const struct log_fmt log_formats[LOG_FORMATS] = {
56 [LOG_FORMAT_RFC3164] = {
57 .name = "rfc3164",
58 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020059 .sep1 = { .area = "[", .data = 1 },
60 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020061 }
62 },
63 [LOG_FORMAT_RFC5424] = {
64 .name = "rfc5424",
65 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020066 .sep1 = { .area = " ", .data = 1 },
67 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020068 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010069 },
70 [LOG_FORMAT_SHORT] = {
71 .name = "short",
72 .pid = {
73 .sep1 = { .area = "", .data = 0 },
74 .sep2 = { .area = " ", .data = 1 },
75 }
76 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010077 [LOG_FORMAT_RAW] = {
78 .name = "raw",
79 .pid = {
80 .sep1 = { .area = "", .data = 0 },
81 .sep2 = { .area = "", .data = 0 },
82 }
83 },
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);
Willy Tarreau2beef582012-12-20 17:22:52 +0100124 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100125};
126
William Lallemandb7ff6a32012-03-02 14:35:21 +0100127int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
128
William Lallemand723b73a2012-02-08 16:37:49 +0100129/* log_format variable names */
130static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200131 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100132
133 /* please keep these lines sorted ! */
134 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
135 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
136 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
137 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
138 { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200139 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200140 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200141 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100142 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200143 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
144 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
145 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
146 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
147 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
148 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200149 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100150 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200151 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
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 */
172 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000173 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000174 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
175 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200176 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100177 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200178 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100179 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
180 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100181 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100182 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
183 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
184 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
185 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
186 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200187 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
188 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100189 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200190 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
191 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
192 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100193 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
194 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
195
196 /* The following tags are deprecated and will be removed soon */
197 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
198 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200199 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
200 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
201 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
202 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100203 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
204 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
205 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
206 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
207 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200208 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100209};
210
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200211char 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
212char 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 +0100213char 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 +0100214char *log_format = NULL;
215
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200216/* Default string used for structured-data part in RFC5424 formatted
217 * syslog messages.
218 */
219char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200220
Willy Tarreau13ef7732018-11-12 07:25:28 +0100221/* total number of dropped logs */
222unsigned int dropped_logs = 0;
223
Dragan Dosen1322d092015-09-22 16:05:32 +0200224/* This is a global syslog header, common to all outgoing messages in
225 * RFC3164 format. It begins with time-based part and is updated by
226 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200227 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200228THREAD_LOCAL char *logheader = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200229THREAD_LOCAL char *logheader_end = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200230
Dragan Dosen1322d092015-09-22 16:05:32 +0200231/* This is a global syslog header for messages in RFC5424 format. It is
232 * updated by update_log_hdr_rfc5424().
233 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200234THREAD_LOCAL char *logheader_rfc5424 = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200235THREAD_LOCAL char *logheader_rfc5424_end = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200236
Dragan Dosen59cee972015-09-19 22:09:02 +0200237/* This is a global syslog message buffer, common to all outgoing
238 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100239 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200240THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100241
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200242/* A global syslog message buffer, common to all RFC5424 syslog messages.
243 * Currently, it is used for generating the structured-data part.
244 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200245THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200246
Christopher Fauletd4696382017-10-24 11:44:05 +0200247/* A global buffer used to store all startup alerts/warnings. It will then be
248 * retrieve on the CLI. */
Willy Tarreaua6483992018-12-15 16:55:36 +0100249static char *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200250
William Lallemand723b73a2012-02-08 16:37:49 +0100251struct logformat_var_args {
252 char *name;
253 int mask;
254};
255
256struct logformat_var_args var_args_list[] = {
257// global
258 { "M", LOG_OPT_MANDATORY },
259 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200260 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100261 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100262 { 0, 0 }
263};
264
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200265/* return the name of the directive used in the current proxy for which we're
266 * currently parsing a header, when it is known.
267 */
268static inline const char *fmt_directive(const struct proxy *curproxy)
269{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100270 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200271 case ARGC_ACL:
272 return "acl";
273 case ARGC_STK:
274 return "stick";
275 case ARGC_TRK:
276 return "track-sc";
277 case ARGC_LOG:
278 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200279 case ARGC_LOGSD:
280 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100281 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100282 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100283 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100284 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200285 case ARGC_UIF:
286 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100287 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200288 return "redirect";
289 case ARGC_CAP:
290 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200291 case ARGC_SRV:
292 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200293 case ARGC_SPOE:
294 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100295 case ARGC_UBK:
296 return "use_backend";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100297 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200298 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100299 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200300}
301
William Lallemand723b73a2012-02-08 16:37:49 +0100302/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100303 * callback used to configure addr source retrieval
304 */
305int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
306{
307 curproxy->options2 |= PR_O2_SRC_ADDR;
308
309 return 0;
310}
311
312
313/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100314 * Parse args in a logformat_var. Returns 0 in error
315 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100316 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100317int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100318{
319 int i = 0;
320 int end = 0;
321 int flags = 0; // 1 = + 2 = -
322 char *sp = NULL; // start pointer
323
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100324 if (args == NULL) {
325 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100326 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100327 }
William Lallemand723b73a2012-02-08 16:37:49 +0100328
329 while (1) {
330 if (*args == '\0')
331 end = 1;
332
333 if (*args == '+') {
334 // add flag
335 sp = args + 1;
336 flags = 1;
337 }
338 if (*args == '-') {
339 // delete flag
340 sp = args + 1;
341 flags = 2;
342 }
343
344 if (*args == '\0' || *args == ',') {
345 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100346 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100347 if (strcmp(sp, var_args_list[i].name) == 0) {
348 if (flags == 1) {
349 node->options |= var_args_list[i].mask;
350 break;
351 } else if (flags == 2) {
352 node->options &= ~var_args_list[i].mask;
353 break;
354 }
355 }
356 }
357 sp = NULL;
358 if (end)
359 break;
360 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100361 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100362 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100363 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100364}
365
366/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100367 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
368 * must pass the args part in the <arg> pointer with its length in <arg_len>,
369 * and varname with its length in <var> and <var_len> respectively. <arg> is
370 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100371 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100372 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100373int 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 +0100374{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100375 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200376 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100377
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100378 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
379 if (strlen(logformat_keywords[j].name) == var_len &&
380 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
381 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200382 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100383 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100384 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200385 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100386 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100387 node->type = logformat_keywords[j].type;
388 node->options = *defoptions;
389 if (arg_len) {
390 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100391 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200392 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100393 }
394 if (node->type == LOG_FMT_GLOBAL) {
395 *defoptions = node->options;
396 free(node->arg);
397 free(node);
398 } else {
399 if (logformat_keywords[j].config_callback &&
400 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200401 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100402 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100403 curproxy->to_log |= logformat_keywords[j].lw;
404 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100405 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100406 if (logformat_keywords[j].replace_by)
Christopher Faulet767a84b2017-11-24 16:50:31 +0100407 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
408 curproxy->conf.args.file, curproxy->conf.args.line,
409 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100410 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100411 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100412 memprintf(err, "format variable '%s' is reserved for HTTP mode",
413 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200414 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100415 }
William Lallemand723b73a2012-02-08 16:37:49 +0100416 }
417 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100418
419 j = var[var_len];
420 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100421 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 +0100422 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200423
424 error_free:
425 if (node) {
426 free(node->arg);
427 free(node);
428 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100429 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100430}
431
432/*
433 * push to the logformat linked list
434 *
435 * start: start pointer
436 * end: end text pointer
437 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100438 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100439 *
440 * LOG_TEXT: copy chars from start to end excluding end.
441 *
442*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100443int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100444{
445 char *str;
446
Willy Tarreaua3571662012-12-20 21:59:12 +0100447 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200448 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100449 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100450 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100451 return 0;
452 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200453 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100454 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100455 str[end - start] = '\0';
456 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100457 node->type = LOG_FMT_TEXT; // type string
458 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100459 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200460 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100461 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100462 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100463 return 0;
464 }
William Lallemand1d705562012-03-12 12:46:41 +0100465 node->type = LOG_FMT_SEPARATOR;
466 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100467 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100468 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100469}
470
471/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100472 * Parse the sample fetch expression <text> and add a node to <list_format> upon
473 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200474 * should work. The curpx->conf.args.ctx must be set by the caller.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100475 *
476 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100477 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100478int 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)
Willy Tarreauc8368452012-12-21 00:09:23 +0100479{
480 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200481 struct sample_expr *expr = NULL;
482 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100483 int cmd_arg;
484
485 cmd[0] = text;
486 cmd[1] = "";
487 cmd_arg = 0;
488
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100489 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args);
Willy Tarreauc8368452012-12-21 00:09:23 +0100490 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100491 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200492 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100493 }
494
Vincent Bernat02779b62016-04-03 13:48:43 +0200495 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100496 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100497 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200498 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100499 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100500 node->type = LOG_FMT_EXPR;
501 node->expr = expr;
502 node->options = options;
503
504 if (arg_len) {
505 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100506 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200507 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100508 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100509 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100510 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
511
Willy Tarreau434c57c2013-01-08 01:10:24 +0100512 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100513 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
514
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100515 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100516 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
517 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200518 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100519 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100520
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200521 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreauc8368452012-12-21 00:09:23 +0100522 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100523 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100524
William Lallemand65ad6e12014-01-31 15:08:02 +0100525 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
526 * needed with some sample fetches (eg: ssl*). We always set it for
527 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100528 */
529 curpx->to_log |= LW_XPRT;
William Lallemand65ad6e12014-01-31 15:08:02 +0100530 curpx->to_log |= LW_REQ;
Willy Tarreauc8368452012-12-21 00:09:23 +0100531 LIST_ADDQ(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100532 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200533
534 error_free:
535 release_sample_expr(expr);
536 if (node) {
537 free(node->arg);
538 free(node);
539 }
540 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100541}
542
543/*
William Lallemand723b73a2012-02-08 16:37:49 +0100544 * Parse the log_format string and fill a linked list.
545 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200546 * You can set arguments using { } : %{many arguments}varname.
547 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100548 *
549 * str: the string to parse
550 * curproxy: the proxy affected
551 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100552 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100553 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100554 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100555 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100556 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100557int 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 +0100558{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100559 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100560 char *arg = NULL; /* start pointer for args */
561 char *var = NULL; /* start pointer for vars */
562 int arg_len = 0;
563 int var_len = 0;
564 int cformat; /* current token format */
565 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100566 struct logformat_node *tmplf, *back;
567
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100568 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100569 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100570 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100571 return 0;
572 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200573 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200574
William Lallemand723b73a2012-02-08 16:37:49 +0100575 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100576 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100577 LIST_DEL(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200578 release_sample_expr(tmplf->expr);
579 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100580 free(tmplf);
581 }
582
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100583 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100584 pformat = cformat;
585
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100586 if (!*str)
587 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100588
Joseph Herlant85b40592018-11-15 12:10:04 -0800589 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100590 * second have all common paths processed at one place. The common paths are the ones
591 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
592 * We use the common LF_INIT state to dispatch to the different final states.
593 */
594 switch (pformat) {
595 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100596 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100597 arg_len = var_len = 0;
598 if (*str == '{') { // optional argument
599 cformat = LF_STARG;
600 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100601 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100602 else if (*str == '[') {
603 cformat = LF_STEXPR;
604 var = str + 1; // store expr in variable name
605 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100606 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100607 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100608 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100609 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100610 else if (*str == '%')
611 cformat = LF_TEXT; // convert this character to a litteral (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100612 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100613 /* single '%' followed by blank or digit, send them both */
614 cformat = LF_TEXT;
615 pformat = LF_TEXT; /* finally we include the previous char as well */
616 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600617 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 +0100618 *str, (int)(str - backfmt), fmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100619 return 0;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100620
621 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100622 else
623 cformat = LF_INIT; // handle other cases of litterals
624 break;
625
626 case LF_STARG: // text immediately following '%{'
627 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100628 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100629 arg_len = str - arg;
630 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100631 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100632 break;
633
634 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100635 if (*str == '[') {
636 cformat = LF_STEXPR;
637 var = str + 1; // store expr in variable name
638 break;
639 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100640 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100641 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100642 var = str;
643 break;
644 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100645 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100646 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100647
Willy Tarreauc8368452012-12-21 00:09:23 +0100648 case LF_STEXPR: // text immediately following '%['
649 if (*str == ']') { // end of arg
650 cformat = LF_EDEXPR;
651 var_len = str - var;
652 *str = 0; // needed for parsing the expression
653 }
654 break;
655
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100656 case LF_VAR: // text part of a variable name
657 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100658 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100659 cformat = LF_INIT; // not variable name anymore
660 break;
661
Willy Tarreauc8368452012-12-21 00:09:23 +0100662 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100663 cformat = LF_INIT;
664 }
665
666 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
667 switch (*str) {
668 case '%': cformat = LF_STARTVAR; break;
669 case ' ': cformat = LF_SEPARATOR; break;
670 case 0 : cformat = LF_END; break;
671 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100672 }
673 }
674
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100675 if (cformat != pformat || pformat == LF_SEPARATOR) {
676 switch (pformat) {
677 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100678 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100679 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100680 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100681 case LF_STEXPR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100682 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100683 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100684 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100685 case LF_TEXT:
686 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100687 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100688 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100689 break;
690 }
691 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100692 }
693 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100694
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100695 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100696 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100697 return 0;
698 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100699 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100700
701 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100702}
703
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200704/*
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200705 * Parse the first range of indexes from a string made of a list of comma seperated
706 * ranges of indexes. Note that an index may be considered as a particular range
707 * with a high limit to the low limit.
708 */
709int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
710{
711 char *end, *p;
712
713 *low = *high = 0;
714
715 p = *arg;
716 end = strchr(p, ',');
717 if (!end)
718 end = p + strlen(p);
719
720 *high = *low = read_uint((const char **)&p, end);
721 if (!*low || (p != end && *p != '-'))
722 goto err;
723
724 if (p == end)
725 goto done;
726
727 p++;
728 *high = read_uint((const char **)&p, end);
729 if (!*high || *high <= *low || p != end)
730 goto err;
731
732 done:
733 if (*end == ',')
734 end++;
735 *arg = end;
736 return 1;
737
738 err:
739 memprintf(err, "wrong sample range '%s'", *arg);
740 return 0;
741}
742
743/*
744 * Returns 1 if the range defined by <low> and <high> overlaps
745 * one of them in <rgs> array of ranges with <sz> the size of this
746 * array, 0 if not.
747 */
748int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
749 unsigned int low, unsigned int high, char **err)
750{
751 size_t i;
752
753 for (i = 0; i < sz; i++) {
754 if ((low >= rgs[i].low && low <= rgs[i].high) ||
755 (high >= rgs[i].low && high <= rgs[i].high)) {
756 memprintf(err, "ranges are overlapping");
757 return 1;
758 }
759 }
760
761 return 0;
762}
763
764int smp_log_range_cmp(const void *a, const void *b)
765{
766 const struct smp_log_range *rg_a = a;
767 const struct smp_log_range *rg_b = b;
768
769 if (rg_a->high < rg_b->low)
770 return -1;
771 else if (rg_a->low > rg_b->high)
772 return 1;
773
774 return 0;
775}
776
777/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200778 * Parse "log" keyword and update <logsrvs> list accordingly.
779 *
780 * When <do_del> is set, it means the "no log" line was parsed, so all log
781 * servers in <logsrvs> are released.
782 *
783 * Otherwise, we try to parse the "log" line. First of all, when the list is not
784 * the global one, we look for the parameter "global". If we find it,
785 * global.logsrvs is copied. Else we parse each arguments.
786 *
787 * The function returns 1 in success case, otherwise, it returns 0 and err is
788 * filled.
789 */
790int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
791{
792 struct sockaddr_storage *sk;
793 struct logsrv *logsrv = NULL;
794 int port1, port2;
795 int cur_arg;
796
797 /*
798 * "no log": delete previous herited or defined syslog
799 * servers.
800 */
801 if (do_del) {
802 struct logsrv *back;
803
804 if (*(args[1]) != 0) {
805 memprintf(err, "'no log' does not expect arguments");
806 goto error;
807 }
808
809 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
810 LIST_DEL(&logsrv->list);
811 free(logsrv);
812 }
813 return 1;
814 }
815
816 /*
817 * "log global": copy global.logrsvs linked list to the end of logsrvs
818 * list. But first, we check (logsrvs != global.logsrvs).
819 */
820 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
821 if (logsrvs == &global.logsrvs) {
822 memprintf(err, "'global' is not supported for a global syslog server");
823 goto error;
824 }
825 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200826 struct logsrv *node;
827
828 list_for_each_entry(node, logsrvs, list) {
829 if (node->ref == logsrv)
830 goto skip_logsrv;
831 }
832
833 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200834 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200835 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200836 LIST_INIT(&node->list);
837 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200838
839 skip_logsrv:
840 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200841 }
842 return 1;
843 }
844
845 /*
846 * "log <address> ...: parse a syslog server line
847 */
848 if (*(args[1]) == 0 || *(args[2]) == 0) {
849 memprintf(err, "expects <address> and <facility> %s as arguments",
850 ((logsrvs == &global.logsrvs) ? "" : "or global"));
851 goto error;
852 }
853
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100854 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
855 if (strcmp(args[1], "stdout") == 0)
856 args[1] = "fd@1";
857 else if (strcmp(args[1], "stderr") == 0)
858 args[1] = "fd@2";
859
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200860 logsrv = calloc(1, sizeof(*logsrv));
861 if (!logsrv) {
862 memprintf(err, "out of memory");
863 goto error;
864 }
865
866 /* skip address for now, it will be parsed at the end */
867 cur_arg = 2;
868
869 /* just after the address, a length may be specified */
870 logsrv->maxlen = MAX_SYSLOG_LEN;
871 if (strcmp(args[cur_arg], "len") == 0) {
872 int len = atoi(args[cur_arg+1]);
873 if (len < 80 || len > 65535) {
874 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
875 args[cur_arg+1]);
876 goto error;
877 }
878 logsrv->maxlen = len;
879 cur_arg += 2;
880 }
881 if (logsrv->maxlen > global.max_syslog_len)
882 global.max_syslog_len = logsrv->maxlen;
883
884 /* after the length, a format may be specified */
885 if (strcmp(args[cur_arg], "format") == 0) {
886 logsrv->format = get_log_format(args[cur_arg+1]);
887 if (logsrv->format < 0) {
888 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
889 goto error;
890 }
891 cur_arg += 2;
892 }
893
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200894 if (strcmp(args[cur_arg], "sample") == 0) {
895 unsigned low, high;
896 char *p, *beg, *end, *smp_sz_str;
897 struct smp_log_range *smp_rgs = NULL;
898 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
899
900 p = args[cur_arg+1];
901 smp_sz_str = strchr(p, ':');
902 if (!smp_sz_str) {
903 memprintf(err, "Missing sample size");
904 goto error;
905 }
906
907 *smp_sz_str++ = '\0';
908
909 end = p + strlen(p);
910
911 while (p != end) {
912 if (!get_logsrv_smp_range(&low, &high, &p, err))
913 goto error;
914
915 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
916 goto error;
917
918 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
919 if (!smp_rgs) {
920 memprintf(err, "out of memory error");
921 goto error;
922 }
923
924 smp_rgs[smp_rgs_sz].low = low;
925 smp_rgs[smp_rgs_sz].high = high;
926 smp_rgs[smp_rgs_sz].sz = high - low + 1;
927 smp_rgs[smp_rgs_sz].curr_idx = 0;
928 if (smp_rgs[smp_rgs_sz].high > smp_sz)
929 smp_sz = smp_rgs[smp_rgs_sz].high;
930 smp_rgs_sz++;
931 }
932
Tim Duesterhus21648002019-06-23 22:10:10 +0200933 if (smp_rgs == NULL) {
934 memprintf(err, "no sampling ranges given");
935 goto error;
936 }
937
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200938 beg = smp_sz_str;
939 end = beg + strlen(beg);
940 new_smp_sz = read_uint((const char **)&beg, end);
941 if (!new_smp_sz || beg != end) {
942 memprintf(err, "wrong sample size '%s' for sample range '%s'",
943 smp_sz_str, args[cur_arg+1]);
944 goto error;
945 }
946
947 if (new_smp_sz < smp_sz) {
948 memprintf(err, "sample size %zu should be greater or equal to "
949 "%zu the maximum of the high ranges limits",
950 new_smp_sz, smp_sz);
951 goto error;
952 }
953 smp_sz = new_smp_sz;
954
955 /* Let's order <smp_rgs> array. */
956 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
957
958 logsrv->lb.smp_rgs = smp_rgs;
959 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
960 logsrv->lb.smp_sz = smp_sz;
961
962 cur_arg += 2;
963 }
Frédéric Lécailled803e472019-04-25 07:42:09 +0200964 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200965 /* parse the facility */
966 logsrv->facility = get_log_facility(args[cur_arg]);
967 if (logsrv->facility < 0) {
968 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
969 goto error;
970 }
971 cur_arg++;
972
973 /* parse the max syslog level (default: debug) */
974 logsrv->level = 7;
975 if (*(args[cur_arg])) {
976 logsrv->level = get_log_level(args[cur_arg]);
977 if (logsrv->level < 0) {
978 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
979 goto error;
980 }
981 cur_arg++;
982 }
983
984 /* parse the limit syslog level (default: emerg) */
985 logsrv->minlvl = 0;
986 if (*(args[cur_arg])) {
987 logsrv->minlvl = get_log_level(args[cur_arg]);
988 if (logsrv->minlvl < 0) {
989 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
990 goto error;
991 }
992 cur_arg++;
993 }
994
995 /* Too many args */
996 if (*(args[cur_arg])) {
997 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
998 goto error;
999 }
1000
1001 /* now, back to the address */
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001002 logsrv->type = LOG_TARGET_DGRAM;
1003 if (strncmp(args[1], "fd@", 3) == 0)
1004 logsrv->type = LOG_TARGET_FD;
1005
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001006 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1007 if (!sk)
1008 goto error;
1009 logsrv->addr = *sk;
1010
1011 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1012 if (port1 != port2) {
1013 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1014 goto error;
1015 }
1016 logsrv->addr = *sk;
1017 if (!port1)
1018 set_host_port(&logsrv->addr, SYSLOG_PORT);
1019 }
1020 LIST_ADDQ(logsrvs, &logsrv->list);
1021 return 1;
1022
1023 error:
1024 free(logsrv);
1025 return 0;
1026}
1027
1028
Christopher Fauletd4696382017-10-24 11:44:05 +02001029/* Generic function to display messages prefixed by a label */
1030static void print_message(const char *label, const char *fmt, va_list argp)
1031{
1032 struct tm tm;
1033 char *head, *msg;
1034
1035 head = msg = NULL;
1036
1037 get_localtime(date.tv_sec, &tm);
1038 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1039 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1040 memvprintf(&msg, fmt, argp);
1041
1042 if (global.mode & MODE_STARTING)
1043 memprintf(&startup_logs, "%s%s%s", (startup_logs ? startup_logs : ""), head, msg);
1044
1045 fprintf(stderr, "%s%s", head, msg);
1046 fflush(stderr);
1047
1048 free(head);
1049 free(msg);
1050}
1051
Willy Tarreaubaaee002006-06-26 02:48:02 +02001052/*
1053 * Displays the message on stderr with the date and pid. Overrides the quiet
1054 * mode during startup.
1055 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001056void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001057{
1058 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001059
1060 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1061 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001062 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001063 va_end(argp);
1064 }
1065}
1066
1067
1068/*
1069 * Displays the message on stderr with the date and pid.
1070 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001071void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001072{
1073 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001074
1075 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1076 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001077 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001078 va_end(argp);
1079 }
1080}
1081
1082/*
William Lallemand9c56a222018-11-21 18:04:52 +01001083 * Displays the message on stderr with the date and pid.
1084 */
1085void ha_notice(const char *fmt, ...)
1086{
1087 va_list argp;
1088
1089 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1090 va_start(argp, fmt);
1091 print_message("NOTICE", fmt, argp);
1092 va_end(argp);
1093 }
1094}
1095
1096/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001097 * Displays the message on <out> only if quiet mode is not set.
1098 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001099void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001100{
1101 va_list argp;
1102
1103 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1104 va_start(argp, fmt);
1105 vfprintf(out, fmt, argp);
1106 fflush(out);
1107 va_end(argp);
1108 }
1109}
1110
1111/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001112 * returns log format for <fmt> or -1 if not found.
1113 */
1114int get_log_format(const char *fmt)
1115{
1116 int format;
1117
1118 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001119 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001120 format--;
1121
1122 return format;
1123}
1124
1125/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001126 * returns log level for <lev> or -1 if not found.
1127 */
1128int get_log_level(const char *lev)
1129{
1130 int level;
1131
1132 level = NB_LOG_LEVELS - 1;
1133 while (level >= 0 && strcmp(log_levels[level], lev))
1134 level--;
1135
1136 return level;
1137}
1138
Willy Tarreaubaaee002006-06-26 02:48:02 +02001139/*
1140 * returns log facility for <fac> or -1 if not found.
1141 */
1142int get_log_facility(const char *fac)
1143{
1144 int facility;
1145
1146 facility = NB_LOG_FACILITIES - 1;
1147 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1148 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001149
Willy Tarreaubaaee002006-06-26 02:48:02 +02001150 return facility;
1151}
1152
William Lallemanda1cc3812012-02-08 16:38:44 +01001153/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001154 * Encode the string.
1155 *
1156 * When using the +E log format option, it will try to escape '"\]'
1157 * characters with '\' as prefix. The same prefix should not be used as
1158 * <escape>.
1159 */
1160static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001161 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001162 const char *string,
1163 struct logformat_node *node)
1164{
1165 if (node->options & LOG_OPT_ESC) {
1166 if (start < stop) {
1167 stop--; /* reserve one byte for the final '\0' */
1168 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001169 if (!ha_bit_test((unsigned char)(*string), map)) {
1170 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001171 *start++ = *string;
1172 else {
1173 if (start + 2 >= stop)
1174 break;
1175 *start++ = '\\';
1176 *start++ = *string;
1177 }
1178 }
1179 else {
1180 if (start + 3 >= stop)
1181 break;
1182 *start++ = escape;
1183 *start++ = hextab[(*string >> 4) & 15];
1184 *start++ = hextab[*string & 15];
1185 }
1186 string++;
1187 }
1188 *start = '\0';
1189 }
1190 }
1191 else {
1192 return encode_string(start, stop, escape, map, string);
1193 }
1194
1195 return start;
1196}
1197
1198/*
1199 * Encode the chunk.
1200 *
1201 * When using the +E log format option, it will try to escape '"\]'
1202 * characters with '\' as prefix. The same prefix should not be used as
1203 * <escape>.
1204 */
1205static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001206 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001207 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001208 struct logformat_node *node)
1209{
1210 char *str, *end;
1211
1212 if (node->options & LOG_OPT_ESC) {
1213 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001214 str = chunk->area;
1215 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001216
1217 stop--; /* reserve one byte for the final '\0' */
1218 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001219 if (!ha_bit_test((unsigned char)(*str), map)) {
1220 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001221 *start++ = *str;
1222 else {
1223 if (start + 2 >= stop)
1224 break;
1225 *start++ = '\\';
1226 *start++ = *str;
1227 }
1228 }
1229 else {
1230 if (start + 3 >= stop)
1231 break;
1232 *start++ = escape;
1233 *start++ = hextab[(*str >> 4) & 15];
1234 *start++ = hextab[*str & 15];
1235 }
1236 str++;
1237 }
1238 *start = '\0';
1239 }
1240 }
1241 else {
1242 return encode_chunk(start, stop, escape, map, chunk);
1243 }
1244
1245 return start;
1246}
1247
1248/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001249 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001250 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001251 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001252 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001253 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001254char *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 +01001255{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001256 if (size < 2)
1257 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001258
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001259 if (node->options & LOG_OPT_QUOTE) {
1260 *(dst++) = '"';
1261 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001262 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001263
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001264 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001265 if (++len > size)
1266 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001267 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001268 char *ret;
1269
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001270 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001271 if (ret == NULL || *ret != '\0')
1272 return NULL;
1273 len = ret - dst;
1274 }
1275 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001276 len = strlcpy2(dst, src, len);
1277 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001278
1279 size -= len;
1280 dst += len;
1281 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001282 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1283 if (size < 2)
1284 return NULL;
1285 *(dst++) = '-';
1286 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001287
1288 if (node->options & LOG_OPT_QUOTE) {
1289 if (size < 2)
1290 return NULL;
1291 *(dst++) = '"';
1292 }
1293
1294 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001295 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001296}
1297
Willy Tarreau26ffa852018-09-05 15:23:10 +02001298static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001299{
1300 return lf_text_len(dst, src, size, size, node);
1301}
1302
William Lallemand5f232402012-04-05 18:02:55 +02001303/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001304 * Write a IP address to the log string
William Lallemand5f232402012-04-05 18:02:55 +02001305 * +X option write in hexadecimal notation, most signifant byte on the left
1306 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001307char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001308{
1309 char *ret = dst;
1310 int iret;
1311 char pn[INET6_ADDRSTRLEN];
1312
1313 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001314 unsigned char *addr = NULL;
1315 switch (sockaddr->sa_family) {
1316 case AF_INET:
1317 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1318 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1319 break;
1320 case AF_INET6:
1321 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1322 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1323 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1324 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1325 break;
1326 default:
1327 return NULL;
1328 }
William Lallemand5f232402012-04-05 18:02:55 +02001329 if (iret < 0 || iret > size)
1330 return NULL;
1331 ret += iret;
1332 } else {
1333 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1334 ret = lf_text(dst, pn, size, node);
1335 if (ret == NULL)
1336 return NULL;
1337 }
1338 return ret;
1339}
1340
1341/*
1342 * Write a port to the log
1343 * +X option write in hexadecimal notation, most signifant byte on the left
1344 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001345char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001346{
1347 char *ret = dst;
1348 int iret;
1349
1350 if (node->options & LOG_OPT_HEXA) {
1351 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1352 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1353 if (iret < 0 || iret > size)
1354 return NULL;
1355 ret += iret;
1356 } else {
1357 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1358 if (ret == NULL)
1359 return NULL;
1360 }
1361 return ret;
1362}
1363
Dragan Dosen1322d092015-09-22 16:05:32 +02001364/* Re-generate time-based part of the syslog header in RFC3164 format at
1365 * the beginning of logheader once a second and return the pointer to the
1366 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001367 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001368static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001369{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001370 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001371 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001372 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001373
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001374 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001375 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001376 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001377 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001378
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001379 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001380 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001381
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001382 if (unlikely(global.log_send_hostname != host.area)) {
1383 host.area = global.log_send_hostname;
1384 host.data = host.area ? strlen(host.area) : 0;
1385 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001386 }
1387
Dragan Dosen59cee972015-09-19 22:09:02 +02001388 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001389 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001390 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001391 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001392 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001393 /* WARNING: depending upon implementations, snprintf may return
1394 * either -1 or the number of bytes that would be needed to store
1395 * the total message. In both cases, we must adjust it.
1396 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001397 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1398 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001399
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001400 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001401 }
1402
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001403 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001404
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001405 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001406}
1407
Dragan Dosen1322d092015-09-22 16:05:32 +02001408/* Re-generate time-based part of the syslog header in RFC5424 format at
1409 * the beginning of logheader_rfc5424 once a second and return the pointer
1410 * to the first character after it.
1411 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001412static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001413{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001414 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001415 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001416
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001417 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001418 /* this string is rebuild only once a second */
1419 struct tm tm;
1420 int hdr_len;
1421
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001422 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001423 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001424 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001425
1426 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001427 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001428 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001429 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001430 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001431 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001432 /* WARNING: depending upon implementations, snprintf may return
1433 * either -1 or the number of bytes that would be needed to store
1434 * the total message. In both cases, we must adjust it.
1435 */
1436 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1437 hdr_len = global.max_syslog_len;
1438
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001439 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001440 }
1441
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001442 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001443
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001444 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001445}
1446
William Lallemand2a4a44f2012-02-06 16:00:33 +01001447/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001448 * This function sends the syslog message using a printf format string. It
1449 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001450 */
1451void send_log(struct proxy *p, int level, const char *format, ...)
1452{
1453 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001454 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001455
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001456 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001457 return;
1458
William Lallemand2a4a44f2012-02-06 16:00:33 +01001459 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001460 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001461 if (data_len < 0 || data_len > global.max_syslog_len)
1462 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001463 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001464
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001465 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001466}
1467
1468/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001469 * This function sends a syslog message to <logsrv>.
1470 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1471 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1472 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001473 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001474 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001475 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001476static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1477 int level, char *message, size_t size, char *sd, size_t sd_size,
1478 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001479{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001480 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1481 static THREAD_LOCAL struct msghdr msghdr = {
1482 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001483 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1484 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001485 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1486 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1487 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001488 time_t time = date.tv_sec;
Dragan Dosen1322d092015-09-22 16:05:32 +02001489 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001490 size_t hdr_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001491 int fac_level;
1492 int *plogfd;
1493 char *pid_sep1 = "", *pid_sep2 = "";
1494 char logheader_short[3];
1495 int sent;
1496 int maxlen;
1497 int hdr_max = 0;
1498 int tag_max = 0;
1499 int pid_sep1_max = 0;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001500 int pid_max = 0;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001501 int pid_sep2_max = 0;
1502 int sd_max = 0;
1503 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001504
1505 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001506
1507 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001508
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001509 if (logsrv->type == LOG_TARGET_FD) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001510 /* the socket's address is a file descriptor */
1511 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
Robert Tsai81ae1952007-12-05 10:47:29 +01001512 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001513 else if (logsrv->addr.ss_family == AF_UNIX)
1514 plogfd = &logfdunix;
1515 else
1516 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001517
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001518 if (unlikely(*plogfd < 0)) {
1519 /* socket not successfully initialized yet */
1520 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1521 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1522 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001523
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001524 if (!once) {
1525 once = 1; /* note: no need for atomic ops here */
1526 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1527 nblogger, strerror(errno), errno);
1528 }
1529 return;
1530 } else {
1531 /* we don't want to receive anything on this socket */
1532 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1533 /* does nothing under Linux, maybe needed for others */
1534 shutdown(*plogfd, SHUT_RD);
1535 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1536 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001537 }
1538
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001539 switch (logsrv->format) {
1540 case LOG_FORMAT_RFC3164:
1541 hdr = logheader;
1542 hdr_ptr = update_log_hdr(time);
1543 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001544
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001545 case LOG_FORMAT_RFC5424:
1546 hdr = logheader_rfc5424;
1547 hdr_ptr = update_log_hdr_rfc5424(time);
1548 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1549 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001550
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001551 case LOG_FORMAT_SHORT:
1552 /* all fields are known, skip the header generation */
1553 hdr = logheader_short;
1554 hdr[0] = '<';
1555 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1556 hdr[2] = '>';
1557 hdr_ptr = hdr;
1558 hdr_max = 3;
1559 maxlen = logsrv->maxlen - hdr_max;
1560 max = MIN(size, maxlen) - 1;
1561 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001562
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001563 case LOG_FORMAT_RAW:
1564 /* all fields are known, skip the header generation */
1565 hdr_ptr = hdr = "";
1566 hdr_max = 0;
1567 maxlen = logsrv->maxlen;
1568 max = MIN(size, maxlen) - 1;
1569 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001570
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001571 default:
1572 return; /* must never happen */
1573 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001574
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001575 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001576
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001577 /* For each target, we may have a different facility.
1578 * We can also have a different log level for each message.
1579 * This induces variations in the message header length.
1580 * Since we don't want to recompute it each time, nor copy it every
1581 * time, we only change the facility in the pre-computed header,
1582 * and we change the pointer to the header accordingly.
1583 */
1584 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1585 hdr_ptr = hdr + 3; /* last digit of the log level */
1586 do {
1587 *hdr_ptr = '0' + fac_level % 10;
1588 fac_level /= 10;
1589 hdr_ptr--;
1590 } while (fac_level && hdr_ptr > hdr);
1591 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001592
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001593 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001594
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001595 /* time-based header */
1596 if (unlikely(hdr_size >= logsrv->maxlen)) {
1597 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1598 sd_max = 0;
1599 goto send;
1600 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001601
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001602 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001603
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001604 /* tag */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001605 tag_max = tag_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001606 if (unlikely(tag_max >= maxlen)) {
1607 tag_max = maxlen - 1;
1608 sd_max = 0;
1609 goto send;
1610 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001611
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001612 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001613
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001614 /* first pid separator */
1615 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1616 if (unlikely(pid_sep1_max >= maxlen)) {
1617 pid_sep1_max = maxlen - 1;
1618 sd_max = 0;
1619 goto send;
1620 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001621
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001622 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1623 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001624
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001625 /* pid */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001626 pid_max = pid_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001627 if (unlikely(pid_size >= maxlen)) {
1628 pid_size = maxlen - 1;
1629 sd_max = 0;
1630 goto send;
1631 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001632
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001633 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001634
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001635 /* second pid separator */
1636 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1637 if (unlikely(pid_sep2_max >= maxlen)) {
1638 pid_sep2_max = maxlen - 1;
1639 sd_max = 0;
1640 goto send;
1641 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001642
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001643 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1644 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001645
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001646 /* structured-data */
1647 if (sd_max >= maxlen) {
1648 sd_max = maxlen - 1;
1649 goto send;
1650 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001651
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001652 max = MIN(size, maxlen - sd_max) - 1;
1653send:
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001654 if (logsrv->addr.ss_family == AF_UNSPEC) {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001655 /* the target is a direct file descriptor */
1656 struct ist msg[7];
1657
1658 msg[0].ptr = hdr_ptr; msg[0].len = hdr_max;
1659 msg[1].ptr = tag_str; msg[1].len = tag_max;
1660 msg[2].ptr = pid_sep1; msg[2].len = pid_sep1_max;
1661 msg[3].ptr = pid_str; msg[3].len = pid_max;
1662 msg[4].ptr = pid_sep2; msg[4].len = pid_sep2_max;
1663 msg[5].ptr = sd; msg[5].len = sd_max;
1664 msg[6].ptr = dataptr; msg[6].len = max;
1665
1666 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001667 }
1668 else {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001669 iovec[0].iov_base = hdr_ptr;
1670 iovec[0].iov_len = hdr_max;
1671 iovec[1].iov_base = tag_str;
1672 iovec[1].iov_len = tag_max;
1673 iovec[2].iov_base = pid_sep1;
1674 iovec[2].iov_len = pid_sep1_max;
1675 iovec[3].iov_base = pid_str;
1676 iovec[3].iov_len = pid_max;
1677 iovec[4].iov_base = pid_sep2;
1678 iovec[4].iov_len = pid_sep2_max;
1679 iovec[5].iov_base = sd;
1680 iovec[5].iov_len = sd_max;
1681 iovec[6].iov_base = dataptr;
1682 iovec[6].iov_len = max;
1683 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1684 iovec[7].iov_len = 1;
1685
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001686 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1687 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001688
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001689 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1690 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001691
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001692 if (sent < 0) {
1693 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001694
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001695 if (errno == EAGAIN)
1696 _HA_ATOMIC_ADD(&dropped_logs, 1);
1697 else if (!once) {
1698 once = 1; /* note: no need for atomic ops here */
1699 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1700 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001701 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001702 }
1703}
Dragan Dosen59cee972015-09-19 22:09:02 +02001704
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001705/*
1706 * This function sends a syslog message.
1707 * It doesn't care about errors nor does it report them.
1708 * The arguments <sd> and <sd_size> are used for the structured-data part
1709 * in RFC5424 formatted syslog messages.
1710 */
1711void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
1712{
1713 struct list *logsrvs = NULL;
1714 struct logsrv *logsrv;
1715 int nblogger;
1716 static THREAD_LOCAL int curr_pid;
1717 static THREAD_LOCAL char pidstr[100];
1718 static THREAD_LOCAL struct buffer pid;
1719 struct buffer *tag = &global.log_tag;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001720
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001721 if (p == NULL) {
1722 if (!LIST_ISEMPTY(&global.logsrvs)) {
1723 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001724 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001725 } else {
1726 if (!LIST_ISEMPTY(&p->logsrvs)) {
1727 logsrvs = &p->logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001728 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001729 if (p->log_tag.area) {
1730 tag = &p->log_tag;
1731 }
1732 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001733
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001734 if (!logsrvs)
1735 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001736
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001737 if (unlikely(curr_pid != getpid())) {
1738 curr_pid = getpid();
1739 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1740 chunk_initstr(&pid, pidstr);
1741 }
1742
1743 /* Send log messages to syslog server. */
1744 nblogger = 0;
1745 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001746 static THREAD_LOCAL int in_range = 1;
1747
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001748 /* we can filter the level of the messages that are sent to each logger */
1749 if (level > logsrv->level)
1750 continue;
1751
Frédéric Lécailled803e472019-04-25 07:42:09 +02001752 if (logsrv->lb.smp_rgs) {
1753 struct smp_log_range *curr_rg;
1754
1755 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1756 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1757 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1758 if (in_range) {
1759 /* Let's consume this range. */
1760 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1761 if (!curr_rg->curr_idx) {
1762 /* If consumed, let's select the next range. */
1763 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1764 }
1765 }
1766 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1767 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1768 }
1769 if (in_range)
1770 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1771 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001772 }
1773}
1774
1775
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001776const 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 +01001777const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1778 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1779 Set-cookie Updated, unknown, unknown */
1780
William Lallemand1d705562012-03-12 12:46:41 +01001781/*
1782 * try to write a character if there is enough space, or goto out
1783 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001784#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001785 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001786 *(tmplog++) = (x); \
1787 } else { \
1788 goto out; \
1789 } \
1790 } while(0)
1791
Dragan Dosen835b9212016-02-12 13:23:03 +01001792
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001793/* Initializes some log data at boot */
1794static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001795{
1796 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001797 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001798
1799 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1800 * inside PARAM-VALUE should be escaped with '\' as prefix.
1801 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1802 * details.
1803 */
1804 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1805
1806 tmp = "\"\\]";
1807 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001808 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001809 tmp++;
1810 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001811
1812 /* initialize the log header encoding map : '{|}"#' should be encoded with
1813 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1814 * URL encoding only requires '"', '#' to be encoded as well as non-
1815 * printable characters above.
1816 */
1817 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1818 memset(url_encode_map, 0, sizeof(url_encode_map));
1819 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001820 ha_bit_set(i, hdr_encode_map);
1821 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001822 }
1823 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001824 ha_bit_set(i, hdr_encode_map);
1825 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001826 }
1827
1828 tmp = "\"#{|}";
1829 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001830 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001831 tmp++;
1832 }
1833
1834 tmp = "\"#";
1835 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001836 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001837 tmp++;
1838 }
1839
1840 /* initialize the http header encoding map. The draft httpbis define the
1841 * header content as:
1842 *
1843 * HTTP-message = start-line
1844 * *( header-field CRLF )
1845 * CRLF
1846 * [ message-body ]
1847 * header-field = field-name ":" OWS field-value OWS
1848 * field-value = *( field-content / obs-fold )
1849 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1850 * obs-fold = CRLF 1*( SP / HTAB )
1851 * field-vchar = VCHAR / obs-text
1852 * VCHAR = %x21-7E
1853 * obs-text = %x80-FF
1854 *
1855 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1856 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001857 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001858 */
1859 memset(http_encode_map, 0, sizeof(http_encode_map));
1860 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001861 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001862 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001863 ha_bit_set(i, http_encode_map);
1864 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001865}
William Lallemand1d705562012-03-12 12:46:41 +01001866
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001867INITCALL0(STG_PREPARE, init_log);
1868
Christopher Faulet0132d062017-07-26 15:33:35 +02001869/* Initialize log buffers used for syslog messages */
1870int init_log_buffers()
1871{
1872 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001873 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001874 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001875 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001876 logline = my_realloc2(logline, global.max_syslog_len + 1);
1877 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1878 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1879 return 0;
1880 return 1;
1881}
1882
1883/* Deinitialize log buffers used for syslog messages */
1884void deinit_log_buffers()
1885{
Olivier Houchard7c497112019-03-07 14:19:24 +01001886 void *tmp_startup_logs;
1887
Christopher Faulet0132d062017-07-26 15:33:35 +02001888 free(logheader);
1889 free(logheader_rfc5424);
1890 free(logline);
1891 free(logline_rfc5424);
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001892 tmp_startup_logs = _HA_ATOMIC_XCHG(&startup_logs, NULL);
Olivier Houchard7c497112019-03-07 14:19:24 +01001893 free(tmp_startup_logs);
1894
Christopher Faulet0132d062017-07-26 15:33:35 +02001895 logheader = NULL;
1896 logheader_rfc5424 = NULL;
1897 logline = NULL;
1898 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001899 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001900}
1901
Willy Tarreaudf974472012-12-28 02:44:01 +01001902/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1903 * <maxsize> characters. Returns the size of the output string in characters,
1904 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001905 * is not zero. It requires a valid session and optionally a stream. If the
1906 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001907 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001908int 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 +02001909{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001910 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001911 struct proxy *be;
1912 struct http_txn *txn;
1913 const struct strm_logs *logs;
Willy Tarreau8fa99842019-07-17 11:47:11 +02001914 struct connection *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001915 unsigned int s_flags;
1916 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001917 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001918 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001919 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001920 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001921 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001922 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001923 int t_request;
1924 int hdr;
1925 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001926 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001927 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001928 char *ret;
1929 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001930 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001931 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001932 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001933
William Lallemandbddd4fd2012-02-27 11:23:10 +01001934 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001935
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001936 if (likely(s)) {
1937 be = s->be;
1938 txn = s->txn;
1939 be_conn = cs_conn(objt_cs(s->si[1].end));
1940 s_flags = s->flags;
1941 uniq_id = s->uniq_id;
1942 logs = &s->logs;
1943 } else {
1944 /* we have no stream so we first need to initialize a few
1945 * things that are needed later. We do increment the request
1946 * ID so that it's uniquely assigned to this request just as
1947 * if the request had reached the point of being processed.
1948 * A request error is reported as it's the only element we have
1949 * here and which justifies emitting such a log.
1950 */
1951 be = fe;
1952 txn = NULL;
1953 be_conn = NULL;
1954 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001955 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001956
1957 /* prepare a valid log structure */
1958 tmp_strm_log.tv_accept = sess->tv_accept;
1959 tmp_strm_log.accept_date = sess->accept_date;
1960 tmp_strm_log.t_handshake = sess->t_handshake;
1961 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1962 tv_zero(&tmp_strm_log.tv_request);
1963 tmp_strm_log.t_queue = -1;
1964 tmp_strm_log.t_connect = -1;
1965 tmp_strm_log.t_data = -1;
1966 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1967 tmp_strm_log.bytes_in = 0;
1968 tmp_strm_log.bytes_out = 0;
1969 tmp_strm_log.prx_queue_pos = 0;
1970 tmp_strm_log.srv_queue_pos = 0;
1971
1972 logs = &tmp_strm_log;
1973 }
1974
William Lallemandbddd4fd2012-02-27 11:23:10 +01001975 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001976 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1977 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001978
William Lallemand1d705562012-03-12 12:46:41 +01001979 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001980
William Lallemandbddd4fd2012-02-27 11:23:10 +01001981 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001982 if (LIST_ISEMPTY(list_format))
1983 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001984
William Lallemand1d705562012-03-12 12:46:41 +01001985 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001986 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001987 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001988 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001989 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001990
Willy Tarreauc8368452012-12-21 00:09:23 +01001991 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01001992 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01001993 if (!last_isspace) {
1994 LOGCHAR(' ');
1995 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001996 }
1997 break;
1998
William Lallemand1d705562012-03-12 12:46:41 +01001999 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002000 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002001 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002002 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002003 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002004 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002005 last_isspace = 0;
2006 break;
2007
Willy Tarreauc8368452012-12-21 00:09:23 +01002008 case LOG_FMT_EXPR: // sample expression, may be request or response
2009 key = NULL;
Olivier Houchardf90db442018-12-15 14:00:06 +01002010 if (tmp->options & LOG_OPT_REQ_CAP && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002011 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Olivier Houchardf90db442018-12-15 14:00:06 +01002012 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002013 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002014 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002015 ret = lf_encode_chunk(tmplog, dst + maxsize,
2016 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002017 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002018 ret = lf_text_len(tmplog,
2019 key ? key->data.u.str.area : NULL,
2020 key ? key->data.u.str.data : 0,
2021 dst + maxsize - tmplog,
2022 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002023 if (ret == 0)
2024 goto out;
2025 tmplog = ret;
2026 last_isspace = 0;
2027 break;
2028
Willy Tarreau2beef582012-12-20 17:22:52 +01002029 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002030 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002031 if (conn && conn_get_src(conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002032 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002033 else
2034 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002035 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002036 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002037 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002038 last_isspace = 0;
2039 break;
2040
Willy Tarreau2beef582012-12-20 17:22:52 +01002041 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002042 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002043 if (conn && conn_get_src(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002044 if (conn->src->ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002045 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002046 } else {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002047 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002048 dst + maxsize - tmplog, tmp);
2049 }
William Lallemand5f232402012-04-05 18:02:55 +02002050 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002051 else
2052 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2053
William Lallemand5f232402012-04-05 18:02:55 +02002054 if (ret == NULL)
2055 goto out;
2056 tmplog = ret;
2057 last_isspace = 0;
2058 break;
2059
Willy Tarreau2beef582012-12-20 17:22:52 +01002060 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002061 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002062 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002063 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002064 }
2065 else
2066 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2067
William Lallemand1d705562012-03-12 12:46:41 +01002068 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002069 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002070 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002071 last_isspace = 0;
2072 break;
2073
Willy Tarreau2beef582012-12-20 17:22:52 +01002074 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002075 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002076 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002077 if (conn->dst->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002078 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002079 else
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002080 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002081 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002082 else
2083 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2084
William Lallemand5f232402012-04-05 18:02:55 +02002085 if (ret == NULL)
2086 goto out;
2087 tmplog = ret;
2088 last_isspace = 0;
2089 break;
2090
Willy Tarreau2beef582012-12-20 17:22:52 +01002091 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002092 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002093 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002094 else
2095 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2096
William Lallemand1d705562012-03-12 12:46:41 +01002097 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002098 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002099 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002100 last_isspace = 0;
2101 break;
2102
Willy Tarreau2beef582012-12-20 17:22:52 +01002103 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002104 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002105 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002106 else
2107 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2108
William Lallemand5f232402012-04-05 18:02:55 +02002109 if (ret == NULL)
2110 goto out;
2111 tmplog = ret;
2112 last_isspace = 0;
2113 break;
2114
Willy Tarreau2beef582012-12-20 17:22:52 +01002115 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002116 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002117 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002118 else
2119 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2120
William Lallemand5f232402012-04-05 18:02:55 +02002121 if (ret == NULL)
2122 goto out;
2123 tmplog = ret;
2124 last_isspace = 0;
2125 break;
2126
Willy Tarreau2beef582012-12-20 17:22:52 +01002127 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002128 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002129 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002130 else
2131 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2132
William Lallemand1d705562012-03-12 12:46:41 +01002133 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002134 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002135 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002136 last_isspace = 0;
2137 break;
2138
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002139 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002140 get_localtime(logs->accept_date.tv_sec, &tm);
2141 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002142 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002143 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002144 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002145 last_isspace = 0;
2146 break;
2147
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002148 case LOG_FMT_tr: // %tr = start of request date
2149 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002150 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 +02002151 get_localtime(tv.tv_sec, &tm);
2152 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2153 if (ret == NULL)
2154 goto out;
2155 tmplog = ret;
2156 last_isspace = 0;
2157 break;
2158
2159 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002160 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002161 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002162 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002163 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002164 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002165 last_isspace = 0;
2166 break;
2167
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002168 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002169 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 +02002170 get_gmtime(tv.tv_sec, &tm);
2171 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2172 if (ret == NULL)
2173 goto out;
2174 tmplog = ret;
2175 last_isspace = 0;
2176 break;
2177
2178 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002179 get_localtime(logs->accept_date.tv_sec, &tm);
2180 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002181 if (ret == NULL)
2182 goto out;
2183 tmplog = ret;
2184 last_isspace = 0;
2185 break;
2186
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002187 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002188 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 +02002189 get_localtime(tv.tv_sec, &tm);
2190 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2191 if (ret == NULL)
2192 goto out;
2193 tmplog = ret;
2194 last_isspace = 0;
2195 break;
2196
William Lallemand5f232402012-04-05 18:02:55 +02002197 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002198 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002199 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002200 if (iret < 0 || iret > dst + maxsize - tmplog)
2201 goto out;
2202 last_isspace = 0;
2203 tmplog += iret;
2204 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002205 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002206 if (ret == NULL)
2207 goto out;
2208 tmplog = ret;
2209 last_isspace = 0;
2210 }
2211 break;
2212
William Lallemand1d705562012-03-12 12:46:41 +01002213 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002214 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002215 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002216 if (iret < 0 || iret > dst + maxsize - tmplog)
2217 goto out;
2218 last_isspace = 0;
2219 tmplog += iret;
2220 } else {
2221 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002222 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002223 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002224 tmplog, 4);
2225 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002226 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002227 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002228 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002229 }
2230 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002231
William Lallemand1d705562012-03-12 12:46:41 +01002232 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002233 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002234 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002235 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002236 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002237 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002238 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002239 break;
2240
Willy Tarreau773d65f2012-10-12 14:56:11 +02002241 case LOG_FMT_FRONTEND_XPRT: // %ft
2242 src = fe->id;
2243 if (tmp->options & LOG_OPT_QUOTE)
2244 LOGCHAR('"');
2245 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2246 if (iret == 0)
2247 goto out;
2248 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002249 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002250 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002251 if (tmp->options & LOG_OPT_QUOTE)
2252 LOGCHAR('"');
2253 last_isspace = 0;
2254 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002255#ifdef USE_OPENSSL
2256 case LOG_FMT_SSL_CIPHER: // %sslc
2257 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002258 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002259 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002260 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002261 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002262 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2263 if (ret == NULL)
2264 goto out;
2265 tmplog = ret;
2266 last_isspace = 0;
2267 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002268
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002269 case LOG_FMT_SSL_VERSION: // %sslv
2270 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002271 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002272 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002273 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002274 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002275 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2276 if (ret == NULL)
2277 goto out;
2278 tmplog = ret;
2279 last_isspace = 0;
2280 break;
2281#endif
William Lallemand1d705562012-03-12 12:46:41 +01002282 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002283 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002284 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002285 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002286 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002287 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002288 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002289 break;
2290
William Lallemand1d705562012-03-12 12:46:41 +01002291 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002292 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002293 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002294 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002295 break;
2296 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002297 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002298 break;
2299 default:
2300 src = "<NOSRV>";
2301 break;
2302 }
William Lallemand5f232402012-04-05 18:02:55 +02002303 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002304 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002305 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002306 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002307 last_isspace = 0;
2308 break;
2309
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002310 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002311 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002312 if (ret == NULL)
2313 goto out;
2314 tmplog = ret;
2315 last_isspace = 0;
2316 break;
2317
2318 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002319 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002320 if (ret == NULL)
2321 goto out;
2322 tmplog = ret;
2323 last_isspace = 0;
2324 break;
2325
2326 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002327 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002328 tmplog, dst + maxsize - tmplog);
2329 if (ret == NULL)
2330 goto out;
2331 tmplog = ret;
2332 last_isspace = 0;
2333 break;
2334
2335 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002336 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002337 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002338 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002339 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002340 last_isspace = 0;
2341 break;
2342
William Lallemand1d705562012-03-12 12:46:41 +01002343 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002344 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002345 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002346 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002347 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002348 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002349 last_isspace = 0;
2350 break;
2351
William Lallemand1d705562012-03-12 12:46:41 +01002352 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002353 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002354 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002355 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002356 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002357 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002358 last_isspace = 0;
2359 break;
2360
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002361 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002362 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002363 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002364 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002365 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002366 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002367 last_isspace = 0;
2368 break;
2369
Willy Tarreau27b639d2016-05-17 17:55:27 +02002370 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002371 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002372 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002373 tmplog, dst + maxsize - tmplog);
2374 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002375 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002376 tmplog, dst + maxsize - tmplog);
2377 if (ret == NULL)
2378 goto out;
2379 tmplog = ret;
2380 last_isspace = 0;
2381 break;
2382
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002383 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2384 if (!(fe->to_log & LW_BYTES))
2385 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002386 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 +02002387 tmplog, dst + maxsize - tmplog);
2388 if (ret == NULL)
2389 goto out;
2390 tmplog = ret;
2391 last_isspace = 0;
2392 break;
2393
2394 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002395 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002396 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002397 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002398 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002399 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002400 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002401 last_isspace = 0;
2402 break;
2403
Willy Tarreau2beef582012-12-20 17:22:52 +01002404 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002405 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002406 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002407 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002408 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002409 last_isspace = 0;
2410 break;
2411
William Lallemand1d705562012-03-12 12:46:41 +01002412 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002413 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002414 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002415 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002416 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002417 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002418 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002419 last_isspace = 0;
2420 break;
2421
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002422 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002423 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002424 if (ret == NULL)
2425 goto out;
2426 tmplog = ret;
2427 last_isspace = 0;
2428 break;
2429
Willy Tarreau2beef582012-12-20 17:22:52 +01002430 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002431 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002432 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002433 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002434 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
Willy Tarreau2beef582012-12-20 17:22:52 +01002439 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002440 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002441 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002442 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002443 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_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002449 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2450 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002451 *tmplog = '\0';
2452 last_isspace = 0;
2453 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002454
William Lallemand1d705562012-03-12 12:46:41 +01002455 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002456 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2457 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002458 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2459 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 +01002460 last_isspace = 0;
2461 break;
2462
William Lallemand1d705562012-03-12 12:46:41 +01002463 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002464 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002465 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002466 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002467 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002468 last_isspace = 0;
2469 break;
2470
William Lallemand1d705562012-03-12 12:46:41 +01002471 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002472 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002473 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002474 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002475 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002476 last_isspace = 0;
2477 break;
2478
William Lallemand1d705562012-03-12 12:46:41 +01002479 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002480 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002481 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002482 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002483 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002484 last_isspace = 0;
2485 break;
2486
William Lallemand1d705562012-03-12 12:46:41 +01002487 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002488 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002489 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002490 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002491 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002492 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002493 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002494 last_isspace = 0;
2495 break;
2496
William Lallemand1d705562012-03-12 12:46:41 +01002497 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002498 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002499 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002500 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002501 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002502 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002503 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002504 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002505 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002506 last_isspace = 0;
2507 break;
2508
William Lallemand1d705562012-03-12 12:46:41 +01002509 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002510 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002511 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002512 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002513 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002514 last_isspace = 0;
2515 break;
2516
William Lallemand1d705562012-03-12 12:46:41 +01002517 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002518 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002519 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002520 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002521 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002522 last_isspace = 0;
2523 break;
2524
William Lallemand1d705562012-03-12 12:46:41 +01002525 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002526 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002527 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002528 if (tmp->options & LOG_OPT_QUOTE)
2529 LOGCHAR('"');
2530 LOGCHAR('{');
2531 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2532 if (hdr)
2533 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002534 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002535 ret = lf_encode_string(tmplog, dst + maxsize,
2536 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002537 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002538 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002539 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002540 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002541 }
2542 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002543 if (tmp->options & LOG_OPT_QUOTE)
2544 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002545 last_isspace = 0;
2546 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002547 break;
2548
William Lallemand1d705562012-03-12 12:46:41 +01002549 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002550 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002551 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002552 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2553 if (hdr > 0)
2554 LOGCHAR(' ');
2555 if (tmp->options & LOG_OPT_QUOTE)
2556 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002557 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002558 ret = lf_encode_string(tmplog, dst + maxsize,
2559 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002560 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002561 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002562 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002563 } else if (!(tmp->options & LOG_OPT_QUOTE))
2564 LOGCHAR('-');
2565 if (tmp->options & LOG_OPT_QUOTE)
2566 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002567 last_isspace = 0;
2568 }
2569 }
2570 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002571
William Lallemand1d705562012-03-12 12:46:41 +01002572
2573 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002574 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002575 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002576 if (tmp->options & LOG_OPT_QUOTE)
2577 LOGCHAR('"');
2578 LOGCHAR('{');
2579 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2580 if (hdr)
2581 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002582 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002583 ret = lf_encode_string(tmplog, dst + maxsize,
2584 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002585 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002586 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002587 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002588 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002589 }
2590 LOGCHAR('}');
2591 last_isspace = 0;
2592 if (tmp->options & LOG_OPT_QUOTE)
2593 LOGCHAR('"');
2594 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002595 break;
2596
William Lallemand1d705562012-03-12 12:46:41 +01002597 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002598 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002599 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002600 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2601 if (hdr > 0)
2602 LOGCHAR(' ');
2603 if (tmp->options & LOG_OPT_QUOTE)
2604 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002605 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002606 ret = lf_encode_string(tmplog, dst + maxsize,
2607 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002608 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002609 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002610 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002611 } else if (!(tmp->options & LOG_OPT_QUOTE))
2612 LOGCHAR('-');
2613 if (tmp->options & LOG_OPT_QUOTE)
2614 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002615 last_isspace = 0;
2616 }
2617 }
2618 break;
2619
William Lallemand1d705562012-03-12 12:46:41 +01002620 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002621 /* Request */
2622 if (tmp->options & LOG_OPT_QUOTE)
2623 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002624 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002625 ret = lf_encode_string(tmplog, dst + maxsize,
2626 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002627 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002628 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002629 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002630 if (tmp->options & LOG_OPT_QUOTE)
2631 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002632 last_isspace = 0;
2633 break;
William Lallemand5f232402012-04-05 18:02:55 +02002634
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002635 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002636 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002637
Willy Tarreaub7636d12015-06-17 19:58:02 +02002638 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002639 LOGCHAR('"');
2640
2641 end = uri + strlen(uri);
2642 // look for the first whitespace character
2643 while (uri < end && !HTTP_IS_SPHT(*uri))
2644 uri++;
2645
2646 // keep advancing past multiple spaces
2647 while (uri < end && HTTP_IS_SPHT(*uri)) {
2648 uri++; nspaces++;
2649 }
2650
2651 // look for first space or question mark after url
2652 spc = uri;
2653 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2654 spc++;
2655
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002656 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002657 chunk.area = "<BADREQ>";
2658 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002659 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002660 chunk.area = uri;
2661 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002662 }
2663
Dragan Dosen835b9212016-02-12 13:23:03 +01002664 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002665 if (ret == NULL || *ret != '\0')
2666 goto out;
2667
2668 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002669 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002670 LOGCHAR('"');
2671
2672 last_isspace = 0;
2673 break;
2674
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002675 case LOG_FMT_HTTP_QUERY: // %HQ
2676 if (tmp->options & LOG_OPT_QUOTE)
2677 LOGCHAR('"');
2678
Willy Tarreau57bc8912016-04-25 17:09:40 +02002679 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002680 chunk.area = "<BADREQ>";
2681 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002682 } else {
2683 uri = txn->uri;
2684 end = uri + strlen(uri);
2685 // look for the first question mark
2686 while (uri < end && *uri != '?')
2687 uri++;
2688
2689 qmark = uri;
2690 // look for first space or question mark after url
2691 while (uri < end && !HTTP_IS_SPHT(*uri))
2692 uri++;
2693
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002694 chunk.area = qmark;
2695 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002696 }
2697
Dragan Dosen835b9212016-02-12 13:23:03 +01002698 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002699 if (ret == NULL || *ret != '\0')
2700 goto out;
2701
2702 tmplog = ret;
2703 if (tmp->options & LOG_OPT_QUOTE)
2704 LOGCHAR('"');
2705
2706 last_isspace = 0;
2707 break;
2708
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002709 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002710 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002711
Willy Tarreaub7636d12015-06-17 19:58:02 +02002712 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002713 LOGCHAR('"');
2714
2715 end = uri + strlen(uri);
2716 // look for the first whitespace character
2717 while (uri < end && !HTTP_IS_SPHT(*uri))
2718 uri++;
2719
2720 // keep advancing past multiple spaces
2721 while (uri < end && HTTP_IS_SPHT(*uri)) {
2722 uri++; nspaces++;
2723 }
2724
2725 // look for first space after url
2726 spc = uri;
2727 while (spc < end && !HTTP_IS_SPHT(*spc))
2728 spc++;
2729
Willy Tarreau57bc8912016-04-25 17:09:40 +02002730 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002731 chunk.area = "<BADREQ>";
2732 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002733 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002734 chunk.area = uri;
2735 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002736 }
2737
Dragan Dosen835b9212016-02-12 13:23:03 +01002738 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002739 if (ret == NULL || *ret != '\0')
2740 goto out;
2741
2742 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002743 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002744 LOGCHAR('"');
2745
2746 last_isspace = 0;
2747 break;
2748
2749 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002750 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002751 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002752 LOGCHAR('"');
2753
2754 end = uri + strlen(uri);
2755 // look for the first whitespace character
2756 spc = uri;
2757 while (spc < end && !HTTP_IS_SPHT(*spc))
2758 spc++;
2759
2760 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002761 chunk.area = "<BADREQ>";
2762 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002763 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002764 chunk.area = uri;
2765 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002766 }
2767
Dragan Dosen835b9212016-02-12 13:23:03 +01002768 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002769 if (ret == NULL || *ret != '\0')
2770 goto out;
2771
2772 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002773 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002774 LOGCHAR('"');
2775
2776 last_isspace = 0;
2777 break;
2778
2779 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002780 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002781 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002782 LOGCHAR('"');
2783
2784 end = uri + strlen(uri);
2785 // look for the first whitespace character
2786 while (uri < end && !HTTP_IS_SPHT(*uri))
2787 uri++;
2788
2789 // keep advancing past multiple spaces
2790 while (uri < end && HTTP_IS_SPHT(*uri)) {
2791 uri++; nspaces++;
2792 }
2793
2794 // look for the next whitespace character
2795 while (uri < end && !HTTP_IS_SPHT(*uri))
2796 uri++;
2797
2798 // keep advancing past multiple spaces
2799 while (uri < end && HTTP_IS_SPHT(*uri))
2800 uri++;
2801
Willy Tarreau57bc8912016-04-25 17:09:40 +02002802 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002803 chunk.area = "<BADREQ>";
2804 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002805 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002806 chunk.area = "HTTP/0.9";
2807 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002808 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002809 chunk.area = uri;
2810 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002811 }
2812
Dragan Dosen835b9212016-02-12 13:23:03 +01002813 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002814 if (ret == NULL || *ret != '\0')
2815 goto out;
2816
2817 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002818 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002819 LOGCHAR('"');
2820
2821 last_isspace = 0;
2822 break;
2823
William Lallemand5f232402012-04-05 18:02:55 +02002824 case LOG_FMT_COUNTER: // %rt
2825 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002826 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002827 if (iret < 0 || iret > dst + maxsize - tmplog)
2828 goto out;
2829 last_isspace = 0;
2830 tmplog += iret;
2831 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002832 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002833 if (ret == NULL)
2834 goto out;
2835 tmplog = ret;
2836 last_isspace = 0;
2837 }
2838 break;
2839
Willy Tarreau7346acb2014-08-28 15:03:15 +02002840 case LOG_FMT_LOGCNT: // %lc
2841 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002842 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002843 if (iret < 0 || iret > dst + maxsize - tmplog)
2844 goto out;
2845 last_isspace = 0;
2846 tmplog += iret;
2847 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002848 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002849 if (ret == NULL)
2850 goto out;
2851 tmplog = ret;
2852 last_isspace = 0;
2853 }
2854 break;
2855
William Lallemand5f232402012-04-05 18:02:55 +02002856 case LOG_FMT_HOSTNAME: // %H
2857 src = hostname;
2858 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2859 if (ret == NULL)
2860 goto out;
2861 tmplog = ret;
2862 last_isspace = 0;
2863 break;
2864
2865 case LOG_FMT_PID: // %pid
2866 if (tmp->options & LOG_OPT_HEXA) {
2867 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2868 if (iret < 0 || iret > dst + maxsize - tmplog)
2869 goto out;
2870 last_isspace = 0;
2871 tmplog += iret;
2872 } else {
2873 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2874 if (ret == NULL)
2875 goto out;
2876 tmplog = ret;
2877 last_isspace = 0;
2878 }
2879 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002880
2881 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002882 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002883 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002884 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002885 if (ret == NULL)
2886 goto out;
2887 tmplog = ret;
2888 last_isspace = 0;
2889 break;
2890
William Lallemandbddd4fd2012-02-27 11:23:10 +01002891 }
2892 }
2893
2894out:
William Lallemand1d705562012-03-12 12:46:41 +01002895 /* *tmplog is a unused character */
2896 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002897 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002898
Willy Tarreaubaaee002006-06-26 02:48:02 +02002899}
2900
William Lallemand1d705562012-03-12 12:46:41 +01002901/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002902 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002903 * Will not log if the frontend has no log defined.
2904 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002905void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002906{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002907 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002908 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002909 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002910
2911 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002912 err = (s->flags & SF_REDISP) ||
2913 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2914 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002915 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002916 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002917
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002918 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002919 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002920
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002921 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002922 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002923
Willy Tarreauabcd5142013-06-11 17:18:02 +02002924 if (s->logs.level) { /* loglevel was overridden */
2925 if (s->logs.level == -1) {
2926 s->logs.logwait = 0; /* logs disabled */
2927 return;
2928 }
2929 level = s->logs.level - 1;
2930 }
2931 else {
2932 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002933 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002934 level = LOG_ERR;
2935 }
William Lallemand1d705562012-03-12 12:46:41 +01002936
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002937 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002938 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002939 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002940 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002941 }
2942
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002943 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2944 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2945 &sess->fe->logformat_sd);
2946 }
2947
Dragan Dosen59cee972015-09-19 22:09:02 +02002948 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002949 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002950 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002951 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002952 s->logs.logwait = 0;
2953 }
2954}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002955
Willy Tarreau53839352018-09-05 19:51:10 +02002956/*
2957 * send a minimalist log for the session. Will not log if the frontend has no
2958 * log defined. It is assumed that this is only used to report anomalies that
2959 * cannot lead to the creation of a regular stream. Because of this the log
2960 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2961 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002962 * function to report unimportant events. It is safe to call this function with
2963 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002964 */
2965void sess_log(struct session *sess)
2966{
2967 int size, level;
2968 int sd_size = 0;
2969
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002970 if (!sess)
2971 return;
2972
Willy Tarreau53839352018-09-05 19:51:10 +02002973 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2974 return;
2975
2976 level = LOG_INFO;
2977 if (sess->fe->options2 & PR_O2_LOGERRORS)
2978 level = LOG_ERR;
2979
2980 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2981 sd_size = sess_build_logline(sess, NULL,
2982 logline_rfc5424, global.max_syslog_len,
2983 &sess->fe->logformat_sd);
2984 }
2985
2986 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2987 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002988 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Willy Tarreau53839352018-09-05 19:51:10 +02002989 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
2990 }
2991}
2992
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002993static int cli_io_handler_show_startup_logs(struct appctx *appctx)
2994{
2995 struct stream_interface *si = appctx->owner;
2996 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
2997
2998 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01002999 si_rx_room_blk(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003000 return 0;
3001 }
3002 return 1;
3003}
3004
3005/* register cli keywords */
3006static struct cli_kw_list cli_kws = {{ },{
3007 { { "show", "startup-logs", NULL },
3008 "show startup-logs : report logs emitted during HAProxy startup",
3009 NULL, cli_io_handler_show_startup_logs },
3010 {{},}
3011}};
3012
Willy Tarreau0108d902018-11-25 19:14:37 +01003013INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3014
Willy Tarreau082b6282019-05-22 14:42:12 +02003015REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3016REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003017
Willy Tarreaubaaee002006-06-26 02:48:02 +02003018/*
3019 * Local variables:
3020 * c-indent-level: 8
3021 * c-basic-offset: 8
3022 * End:
3023 */