blob: cffd8f1483dd994f31fd0be03f5ed0a85499066c [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>
William Lallemand5f232402012-04-05 18:02:55 +020039#include <proto/frontend.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020040#include <proto/log.h>
Willy Tarreauc8368452012-12-21 00:09:23 +010041#include <proto/sample.h>
Willy Tarreaufb0afa72015-04-03 14:46:27 +020042#include <proto/stream.h>
Willy Tarreau827aee92011-03-10 16:55:02 +010043#include <proto/stream_interface.h>
Willy Tarreau773d65f2012-10-12 14:56:11 +020044#ifdef USE_OPENSSL
45#include <proto/ssl_sock.h>
46#endif
Willy Tarreaubaaee002006-06-26 02:48:02 +020047
Dragan Dosen43885c72015-10-01 13:18:13 +020048struct log_fmt {
49 char *name;
50 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020051 struct buffer sep1; /* first pid separator */
52 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020053 } pid;
54};
55
56static const struct log_fmt log_formats[LOG_FORMATS] = {
57 [LOG_FORMAT_RFC3164] = {
58 .name = "rfc3164",
59 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020060 .sep1 = { .area = "[", .data = 1 },
61 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020062 }
63 },
64 [LOG_FORMAT_RFC5424] = {
65 .name = "rfc5424",
66 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020067 .sep1 = { .area = " ", .data = 1 },
68 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020069 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010070 },
71 [LOG_FORMAT_SHORT] = {
72 .name = "short",
73 .pid = {
74 .sep1 = { .area = "", .data = 0 },
75 .sep2 = { .area = " ", .data = 1 },
76 }
77 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010078 [LOG_FORMAT_RAW] = {
79 .name = "raw",
80 .pid = {
81 .sep1 = { .area = "", .data = 0 },
82 .sep2 = { .area = "", .data = 0 },
83 }
84 },
Dragan Dosen1322d092015-09-22 16:05:32 +020085};
86
Dragan Dosen835b9212016-02-12 13:23:03 +010087#define FD_SETS_ARE_BITFIELDS
88#ifdef FD_SETS_ARE_BITFIELDS
89/*
90 * This map is used with all the FD_* macros to check whether a particular bit
91 * is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
92 * which should be escaped. When FD_ISSET() returns non-zero, it means that the
93 * byte should be escaped. Be careful to always pass bytes from 0 to 255
94 * exclusively to the macros.
95 */
96fd_set rfc5424_escape_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
Willy Tarreaue10cd482018-09-10 18:16:53 +020097fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
98fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
99fd_set http_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
Dragan Dosen835b9212016-02-12 13:23:03 +0100100
101#else
102#error "Check if your OS uses bitfields for fd_sets"
103#endif
104
Willy Tarreaubaaee002006-06-26 02:48:02 +0200105const char *log_facilities[NB_LOG_FACILITIES] = {
106 "kern", "user", "mail", "daemon",
107 "auth", "syslog", "lpr", "news",
108 "uucp", "cron", "auth2", "ftp",
109 "ntp", "audit", "alert", "cron2",
110 "local0", "local1", "local2", "local3",
111 "local4", "local5", "local6", "local7"
112};
113
Willy Tarreaubaaee002006-06-26 02:48:02 +0200114const char *log_levels[NB_LOG_LEVELS] = {
115 "emerg", "alert", "crit", "err",
116 "warning", "notice", "info", "debug"
117};
118
Willy Tarreau570f2212013-06-10 16:42:09 +0200119const 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 +0200120const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200121
William Lallemand723b73a2012-02-08 16:37:49 +0100122
123/* log_format */
124struct logformat_type {
125 char *name;
126 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100127 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200128 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100129 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
Willy Tarreau2beef582012-12-20 17:22:52 +0100130 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100131};
132
William Lallemandb7ff6a32012-03-02 14:35:21 +0100133int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
134
William Lallemand723b73a2012-02-08 16:37:49 +0100135/* log_format variable names */
136static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200137 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100138
139 /* please keep these lines sorted ! */
140 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
141 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
142 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
143 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
144 { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200145 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200146 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200147 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100148 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200149 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
150 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
151 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
152 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
153 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
154 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200155 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100156 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200157 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Willy Tarreau2beef582012-12-20 17:22:52 +0100158 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
159 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200160 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100161 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200162 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100163 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
164 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200165 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200166 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
167 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100168 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
169 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200170 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
171 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100172 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200173 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
174 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
175 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
176 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000177 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
178 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000179 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000180 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
181 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200182 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100183 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200184 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100185 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
186 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100187 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100188 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
189 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
190 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
191 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
192 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200193 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
194 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100195 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200196 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
197 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
198 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100199 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
200 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
201
202 /* The following tags are deprecated and will be removed soon */
203 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
204 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200205 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
206 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
207 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
208 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100209 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
210 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
211 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
212 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
213 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200214 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100215};
216
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200217char 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
218char 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 +0100219char 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 +0100220char *log_format = NULL;
221
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200222/* Default string used for structured-data part in RFC5424 formatted
223 * syslog messages.
224 */
225char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200226
Willy Tarreau13ef7732018-11-12 07:25:28 +0100227/* total number of dropped logs */
228unsigned int dropped_logs = 0;
229
Dragan Dosen1322d092015-09-22 16:05:32 +0200230/* This is a global syslog header, common to all outgoing messages in
231 * RFC3164 format. It begins with time-based part and is updated by
232 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200233 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200234THREAD_LOCAL char *logheader = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200235
Dragan Dosen1322d092015-09-22 16:05:32 +0200236/* This is a global syslog header for messages in RFC5424 format. It is
237 * updated by update_log_hdr_rfc5424().
238 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200239THREAD_LOCAL char *logheader_rfc5424 = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200240
Dragan Dosen59cee972015-09-19 22:09:02 +0200241/* This is a global syslog message buffer, common to all outgoing
242 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100243 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200244THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100245
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200246/* A global syslog message buffer, common to all RFC5424 syslog messages.
247 * Currently, it is used for generating the structured-data part.
248 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200249THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200250
Christopher Fauletd4696382017-10-24 11:44:05 +0200251/* A global buffer used to store all startup alerts/warnings. It will then be
252 * retrieve on the CLI. */
Willy Tarreaua6483992018-12-15 16:55:36 +0100253static char *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200254
William Lallemand723b73a2012-02-08 16:37:49 +0100255struct logformat_var_args {
256 char *name;
257 int mask;
258};
259
260struct logformat_var_args var_args_list[] = {
261// global
262 { "M", LOG_OPT_MANDATORY },
263 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200264 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100265 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100266 { 0, 0 }
267};
268
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200269/* return the name of the directive used in the current proxy for which we're
270 * currently parsing a header, when it is known.
271 */
272static inline const char *fmt_directive(const struct proxy *curproxy)
273{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100274 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200275 case ARGC_ACL:
276 return "acl";
277 case ARGC_STK:
278 return "stick";
279 case ARGC_TRK:
280 return "track-sc";
281 case ARGC_LOG:
282 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200283 case ARGC_LOGSD:
284 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100285 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100286 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100287 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100288 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200289 case ARGC_UIF:
290 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100291 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200292 return "redirect";
293 case ARGC_CAP:
294 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200295 case ARGC_SRV:
296 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200297 case ARGC_SPOE:
298 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100299 case ARGC_UBK:
300 return "use_backend";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100301 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200302 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100303 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200304}
305
William Lallemand723b73a2012-02-08 16:37:49 +0100306/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100307 * callback used to configure addr source retrieval
308 */
309int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
310{
311 curproxy->options2 |= PR_O2_SRC_ADDR;
312
313 return 0;
314}
315
316
317/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100318 * Parse args in a logformat_var. Returns 0 in error
319 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100320 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100321int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100322{
323 int i = 0;
324 int end = 0;
325 int flags = 0; // 1 = + 2 = -
326 char *sp = NULL; // start pointer
327
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100328 if (args == NULL) {
329 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100330 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100331 }
William Lallemand723b73a2012-02-08 16:37:49 +0100332
333 while (1) {
334 if (*args == '\0')
335 end = 1;
336
337 if (*args == '+') {
338 // add flag
339 sp = args + 1;
340 flags = 1;
341 }
342 if (*args == '-') {
343 // delete flag
344 sp = args + 1;
345 flags = 2;
346 }
347
348 if (*args == '\0' || *args == ',') {
349 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100350 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100351 if (strcmp(sp, var_args_list[i].name) == 0) {
352 if (flags == 1) {
353 node->options |= var_args_list[i].mask;
354 break;
355 } else if (flags == 2) {
356 node->options &= ~var_args_list[i].mask;
357 break;
358 }
359 }
360 }
361 sp = NULL;
362 if (end)
363 break;
364 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100365 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100366 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100367 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100368}
369
370/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100371 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
372 * must pass the args part in the <arg> pointer with its length in <arg_len>,
373 * and varname with its length in <var> and <var_len> respectively. <arg> is
374 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100375 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100376 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100377int 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 +0100378{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100379 int j;
380 struct logformat_node *node;
William Lallemand723b73a2012-02-08 16:37:49 +0100381
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100382 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
383 if (strlen(logformat_keywords[j].name) == var_len &&
384 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
385 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200386 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100387 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100388 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100389 return 0;
390 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100391 node->type = logformat_keywords[j].type;
392 node->options = *defoptions;
393 if (arg_len) {
394 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100395 if (!parse_logformat_var_args(node->arg, node, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100396 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100397 }
398 if (node->type == LOG_FMT_GLOBAL) {
399 *defoptions = node->options;
400 free(node->arg);
401 free(node);
402 } else {
403 if (logformat_keywords[j].config_callback &&
404 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100405 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100406 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100407 curproxy->to_log |= logformat_keywords[j].lw;
408 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100409 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100410 if (logformat_keywords[j].replace_by)
Christopher Faulet767a84b2017-11-24 16:50:31 +0100411 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
412 curproxy->conf.args.file, curproxy->conf.args.line,
413 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100414 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100415 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100416 memprintf(err, "format variable '%s' is reserved for HTTP mode",
417 logformat_keywords[j].name);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100418 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100419 }
William Lallemand723b73a2012-02-08 16:37:49 +0100420 }
421 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100422
423 j = var[var_len];
424 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100425 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 +0100426 var[var_len] = j;
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100427 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100428}
429
430/*
431 * push to the logformat linked list
432 *
433 * start: start pointer
434 * end: end text pointer
435 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100436 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100437 *
438 * LOG_TEXT: copy chars from start to end excluding end.
439 *
440*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100441int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100442{
443 char *str;
444
Willy Tarreaua3571662012-12-20 21:59:12 +0100445 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200446 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100447 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100448 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100449 return 0;
450 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200451 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100452 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100453 str[end - start] = '\0';
454 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100455 node->type = LOG_FMT_TEXT; // type string
456 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100457 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200458 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100459 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100460 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100461 return 0;
462 }
William Lallemand1d705562012-03-12 12:46:41 +0100463 node->type = LOG_FMT_SEPARATOR;
464 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100465 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100466 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100467}
468
469/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100470 * Parse the sample fetch expression <text> and add a node to <list_format> upon
471 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200472 * should work. The curpx->conf.args.ctx must be set by the caller.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100473 *
474 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100475 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100476int 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 +0100477{
478 char *cmd[2];
479 struct sample_expr *expr;
480 struct logformat_node *node;
481 int cmd_arg;
482
483 cmd[0] = text;
484 cmd[1] = "";
485 cmd_arg = 0;
486
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100487 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 +0100488 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100489 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100490 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100491 }
492
Vincent Bernat02779b62016-04-03 13:48:43 +0200493 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100494 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100495 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100496 return 0;
497 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100498 node->type = LOG_FMT_EXPR;
499 node->expr = expr;
500 node->options = options;
501
502 if (arg_len) {
503 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100504 if (!parse_logformat_var_args(node->arg, node, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100505 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100506 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100507 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100508 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
509
Willy Tarreau434c57c2013-01-08 01:10:24 +0100510 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100511 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
512
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100513 if (!(expr->fetch->val & cap)) {
David Carlier93e8b882017-09-21 14:36:43 +0000514 free(node);
515 node = NULL;
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));
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100518 return 0;
519 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100520
Willy Tarreauc8368452012-12-21 00:09:23 +0100521 /* check if we need to allocate an hdr_idx struct for HTTP parsing */
522 /* 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;
Willy Tarreauc8368452012-12-21 00:09:23 +0100533}
534
535/*
William Lallemand723b73a2012-02-08 16:37:49 +0100536 * Parse the log_format string and fill a linked list.
537 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200538 * You can set arguments using { } : %{many arguments}varname.
539 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100540 *
541 * str: the string to parse
542 * curproxy: the proxy affected
543 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100544 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100545 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100546 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100547 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100548 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100549int 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 +0100550{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100551 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100552 char *arg = NULL; /* start pointer for args */
553 char *var = NULL; /* start pointer for vars */
554 int arg_len = 0;
555 int var_len = 0;
556 int cformat; /* current token format */
557 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100558 struct logformat_node *tmplf, *back;
559
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100560 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100561 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100562 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100563 return 0;
564 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200565 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200566
William Lallemand723b73a2012-02-08 16:37:49 +0100567 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100568 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100569 LIST_DEL(&tmplf->list);
570 free(tmplf);
571 }
572
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100573 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100574 pformat = cformat;
575
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100576 if (!*str)
577 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100578
Joseph Herlant85b40592018-11-15 12:10:04 -0800579 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100580 * second have all common paths processed at one place. The common paths are the ones
581 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
582 * We use the common LF_INIT state to dispatch to the different final states.
583 */
584 switch (pformat) {
585 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100586 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100587 arg_len = var_len = 0;
588 if (*str == '{') { // optional argument
589 cformat = LF_STARG;
590 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100591 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100592 else if (*str == '[') {
593 cformat = LF_STEXPR;
594 var = str + 1; // store expr in variable name
595 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100596 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100597 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100598 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100599 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100600 else if (*str == '%')
601 cformat = LF_TEXT; // convert this character to a litteral (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100602 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100603 /* single '%' followed by blank or digit, send them both */
604 cformat = LF_TEXT;
605 pformat = LF_TEXT; /* finally we include the previous char as well */
606 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600607 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 +0100608 *str, (int)(str - backfmt), fmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100609 return 0;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100610
611 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100612 else
613 cformat = LF_INIT; // handle other cases of litterals
614 break;
615
616 case LF_STARG: // text immediately following '%{'
617 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100618 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100619 arg_len = str - arg;
620 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100621 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100622 break;
623
624 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100625 if (*str == '[') {
626 cformat = LF_STEXPR;
627 var = str + 1; // store expr in variable name
628 break;
629 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100630 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100631 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100632 var = str;
633 break;
634 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100635 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100636 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100637
Willy Tarreauc8368452012-12-21 00:09:23 +0100638 case LF_STEXPR: // text immediately following '%['
639 if (*str == ']') { // end of arg
640 cformat = LF_EDEXPR;
641 var_len = str - var;
642 *str = 0; // needed for parsing the expression
643 }
644 break;
645
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100646 case LF_VAR: // text part of a variable name
647 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100648 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100649 cformat = LF_INIT; // not variable name anymore
650 break;
651
Willy Tarreauc8368452012-12-21 00:09:23 +0100652 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100653 cformat = LF_INIT;
654 }
655
656 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
657 switch (*str) {
658 case '%': cformat = LF_STARTVAR; break;
659 case ' ': cformat = LF_SEPARATOR; break;
660 case 0 : cformat = LF_END; break;
661 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100662 }
663 }
664
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100665 if (cformat != pformat || pformat == LF_SEPARATOR) {
666 switch (pformat) {
667 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100668 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100669 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100670 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100671 case LF_STEXPR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100672 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 +0100673 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100674 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100675 case LF_TEXT:
676 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100677 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100678 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100679 break;
680 }
681 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100682 }
683 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100684
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100685 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100686 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100687 return 0;
688 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100689 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100690
691 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100692}
693
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200694/*
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200695 * Parse the first range of indexes from a string made of a list of comma seperated
696 * ranges of indexes. Note that an index may be considered as a particular range
697 * with a high limit to the low limit.
698 */
699int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
700{
701 char *end, *p;
702
703 *low = *high = 0;
704
705 p = *arg;
706 end = strchr(p, ',');
707 if (!end)
708 end = p + strlen(p);
709
710 *high = *low = read_uint((const char **)&p, end);
711 if (!*low || (p != end && *p != '-'))
712 goto err;
713
714 if (p == end)
715 goto done;
716
717 p++;
718 *high = read_uint((const char **)&p, end);
719 if (!*high || *high <= *low || p != end)
720 goto err;
721
722 done:
723 if (*end == ',')
724 end++;
725 *arg = end;
726 return 1;
727
728 err:
729 memprintf(err, "wrong sample range '%s'", *arg);
730 return 0;
731}
732
733/*
734 * Returns 1 if the range defined by <low> and <high> overlaps
735 * one of them in <rgs> array of ranges with <sz> the size of this
736 * array, 0 if not.
737 */
738int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
739 unsigned int low, unsigned int high, char **err)
740{
741 size_t i;
742
743 for (i = 0; i < sz; i++) {
744 if ((low >= rgs[i].low && low <= rgs[i].high) ||
745 (high >= rgs[i].low && high <= rgs[i].high)) {
746 memprintf(err, "ranges are overlapping");
747 return 1;
748 }
749 }
750
751 return 0;
752}
753
754int smp_log_range_cmp(const void *a, const void *b)
755{
756 const struct smp_log_range *rg_a = a;
757 const struct smp_log_range *rg_b = b;
758
759 if (rg_a->high < rg_b->low)
760 return -1;
761 else if (rg_a->low > rg_b->high)
762 return 1;
763
764 return 0;
765}
766
767/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200768 * Parse "log" keyword and update <logsrvs> list accordingly.
769 *
770 * When <do_del> is set, it means the "no log" line was parsed, so all log
771 * servers in <logsrvs> are released.
772 *
773 * Otherwise, we try to parse the "log" line. First of all, when the list is not
774 * the global one, we look for the parameter "global". If we find it,
775 * global.logsrvs is copied. Else we parse each arguments.
776 *
777 * The function returns 1 in success case, otherwise, it returns 0 and err is
778 * filled.
779 */
780int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
781{
782 struct sockaddr_storage *sk;
783 struct logsrv *logsrv = NULL;
784 int port1, port2;
785 int cur_arg;
786
787 /*
788 * "no log": delete previous herited or defined syslog
789 * servers.
790 */
791 if (do_del) {
792 struct logsrv *back;
793
794 if (*(args[1]) != 0) {
795 memprintf(err, "'no log' does not expect arguments");
796 goto error;
797 }
798
799 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
800 LIST_DEL(&logsrv->list);
801 free(logsrv);
802 }
803 return 1;
804 }
805
806 /*
807 * "log global": copy global.logrsvs linked list to the end of logsrvs
808 * list. But first, we check (logsrvs != global.logsrvs).
809 */
810 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
811 if (logsrvs == &global.logsrvs) {
812 memprintf(err, "'global' is not supported for a global syslog server");
813 goto error;
814 }
815 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200816 struct logsrv *node;
817
818 list_for_each_entry(node, logsrvs, list) {
819 if (node->ref == logsrv)
820 goto skip_logsrv;
821 }
822
823 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200824 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200825 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200826 LIST_INIT(&node->list);
827 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200828
829 skip_logsrv:
830 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200831 }
832 return 1;
833 }
834
835 /*
836 * "log <address> ...: parse a syslog server line
837 */
838 if (*(args[1]) == 0 || *(args[2]) == 0) {
839 memprintf(err, "expects <address> and <facility> %s as arguments",
840 ((logsrvs == &global.logsrvs) ? "" : "or global"));
841 goto error;
842 }
843
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100844 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
845 if (strcmp(args[1], "stdout") == 0)
846 args[1] = "fd@1";
847 else if (strcmp(args[1], "stderr") == 0)
848 args[1] = "fd@2";
849
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200850 logsrv = calloc(1, sizeof(*logsrv));
851 if (!logsrv) {
852 memprintf(err, "out of memory");
853 goto error;
854 }
855
856 /* skip address for now, it will be parsed at the end */
857 cur_arg = 2;
858
859 /* just after the address, a length may be specified */
860 logsrv->maxlen = MAX_SYSLOG_LEN;
861 if (strcmp(args[cur_arg], "len") == 0) {
862 int len = atoi(args[cur_arg+1]);
863 if (len < 80 || len > 65535) {
864 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
865 args[cur_arg+1]);
866 goto error;
867 }
868 logsrv->maxlen = len;
869 cur_arg += 2;
870 }
871 if (logsrv->maxlen > global.max_syslog_len)
872 global.max_syslog_len = logsrv->maxlen;
873
874 /* after the length, a format may be specified */
875 if (strcmp(args[cur_arg], "format") == 0) {
876 logsrv->format = get_log_format(args[cur_arg+1]);
877 if (logsrv->format < 0) {
878 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
879 goto error;
880 }
881 cur_arg += 2;
882 }
883
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200884 if (strcmp(args[cur_arg], "sample") == 0) {
885 unsigned low, high;
886 char *p, *beg, *end, *smp_sz_str;
887 struct smp_log_range *smp_rgs = NULL;
888 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
889
890 p = args[cur_arg+1];
891 smp_sz_str = strchr(p, ':');
892 if (!smp_sz_str) {
893 memprintf(err, "Missing sample size");
894 goto error;
895 }
896
897 *smp_sz_str++ = '\0';
898
899 end = p + strlen(p);
900
901 while (p != end) {
902 if (!get_logsrv_smp_range(&low, &high, &p, err))
903 goto error;
904
905 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
906 goto error;
907
908 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
909 if (!smp_rgs) {
910 memprintf(err, "out of memory error");
911 goto error;
912 }
913
914 smp_rgs[smp_rgs_sz].low = low;
915 smp_rgs[smp_rgs_sz].high = high;
916 smp_rgs[smp_rgs_sz].sz = high - low + 1;
917 smp_rgs[smp_rgs_sz].curr_idx = 0;
918 if (smp_rgs[smp_rgs_sz].high > smp_sz)
919 smp_sz = smp_rgs[smp_rgs_sz].high;
920 smp_rgs_sz++;
921 }
922
923 beg = smp_sz_str;
924 end = beg + strlen(beg);
925 new_smp_sz = read_uint((const char **)&beg, end);
926 if (!new_smp_sz || beg != end) {
927 memprintf(err, "wrong sample size '%s' for sample range '%s'",
928 smp_sz_str, args[cur_arg+1]);
929 goto error;
930 }
931
932 if (new_smp_sz < smp_sz) {
933 memprintf(err, "sample size %zu should be greater or equal to "
934 "%zu the maximum of the high ranges limits",
935 new_smp_sz, smp_sz);
936 goto error;
937 }
938 smp_sz = new_smp_sz;
939
940 /* Let's order <smp_rgs> array. */
941 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
942
943 logsrv->lb.smp_rgs = smp_rgs;
944 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
945 logsrv->lb.smp_sz = smp_sz;
946
947 cur_arg += 2;
948 }
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200949 /* parse the facility */
950 logsrv->facility = get_log_facility(args[cur_arg]);
951 if (logsrv->facility < 0) {
952 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
953 goto error;
954 }
955 cur_arg++;
956
957 /* parse the max syslog level (default: debug) */
958 logsrv->level = 7;
959 if (*(args[cur_arg])) {
960 logsrv->level = get_log_level(args[cur_arg]);
961 if (logsrv->level < 0) {
962 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
963 goto error;
964 }
965 cur_arg++;
966 }
967
968 /* parse the limit syslog level (default: emerg) */
969 logsrv->minlvl = 0;
970 if (*(args[cur_arg])) {
971 logsrv->minlvl = get_log_level(args[cur_arg]);
972 if (logsrv->minlvl < 0) {
973 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
974 goto error;
975 }
976 cur_arg++;
977 }
978
979 /* Too many args */
980 if (*(args[cur_arg])) {
981 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
982 goto error;
983 }
984
985 /* now, back to the address */
986 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
987 if (!sk)
988 goto error;
989 logsrv->addr = *sk;
990
991 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
992 if (port1 != port2) {
993 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
994 goto error;
995 }
996 logsrv->addr = *sk;
997 if (!port1)
998 set_host_port(&logsrv->addr, SYSLOG_PORT);
999 }
1000 LIST_ADDQ(logsrvs, &logsrv->list);
1001 return 1;
1002
1003 error:
1004 free(logsrv);
1005 return 0;
1006}
1007
1008
Christopher Fauletd4696382017-10-24 11:44:05 +02001009/* Generic function to display messages prefixed by a label */
1010static void print_message(const char *label, const char *fmt, va_list argp)
1011{
1012 struct tm tm;
1013 char *head, *msg;
1014
1015 head = msg = NULL;
1016
1017 get_localtime(date.tv_sec, &tm);
1018 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1019 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1020 memvprintf(&msg, fmt, argp);
1021
1022 if (global.mode & MODE_STARTING)
1023 memprintf(&startup_logs, "%s%s%s", (startup_logs ? startup_logs : ""), head, msg);
1024
1025 fprintf(stderr, "%s%s", head, msg);
1026 fflush(stderr);
1027
1028 free(head);
1029 free(msg);
1030}
1031
Willy Tarreaubaaee002006-06-26 02:48:02 +02001032/*
1033 * Displays the message on stderr with the date and pid. Overrides the quiet
1034 * mode during startup.
1035 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001036void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001037{
1038 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001039
1040 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1041 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001042 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001043 va_end(argp);
1044 }
1045}
1046
1047
1048/*
1049 * Displays the message on stderr with the date and pid.
1050 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001051void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001052{
1053 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001054
1055 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1056 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001057 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001058 va_end(argp);
1059 }
1060}
1061
1062/*
William Lallemand9c56a222018-11-21 18:04:52 +01001063 * Displays the message on stderr with the date and pid.
1064 */
1065void ha_notice(const char *fmt, ...)
1066{
1067 va_list argp;
1068
1069 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1070 va_start(argp, fmt);
1071 print_message("NOTICE", fmt, argp);
1072 va_end(argp);
1073 }
1074}
1075
1076/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001077 * Displays the message on <out> only if quiet mode is not set.
1078 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001079void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001080{
1081 va_list argp;
1082
1083 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1084 va_start(argp, fmt);
1085 vfprintf(out, fmt, argp);
1086 fflush(out);
1087 va_end(argp);
1088 }
1089}
1090
1091/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001092 * returns log format for <fmt> or -1 if not found.
1093 */
1094int get_log_format(const char *fmt)
1095{
1096 int format;
1097
1098 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001099 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001100 format--;
1101
1102 return format;
1103}
1104
1105/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001106 * returns log level for <lev> or -1 if not found.
1107 */
1108int get_log_level(const char *lev)
1109{
1110 int level;
1111
1112 level = NB_LOG_LEVELS - 1;
1113 while (level >= 0 && strcmp(log_levels[level], lev))
1114 level--;
1115
1116 return level;
1117}
1118
Willy Tarreaubaaee002006-06-26 02:48:02 +02001119/*
1120 * returns log facility for <fac> or -1 if not found.
1121 */
1122int get_log_facility(const char *fac)
1123{
1124 int facility;
1125
1126 facility = NB_LOG_FACILITIES - 1;
1127 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1128 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001129
Willy Tarreaubaaee002006-06-26 02:48:02 +02001130 return facility;
1131}
1132
William Lallemanda1cc3812012-02-08 16:38:44 +01001133/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001134 * Encode the string.
1135 *
1136 * When using the +E log format option, it will try to escape '"\]'
1137 * characters with '\' as prefix. The same prefix should not be used as
1138 * <escape>.
1139 */
1140static char *lf_encode_string(char *start, char *stop,
1141 const char escape, const fd_set *map,
1142 const char *string,
1143 struct logformat_node *node)
1144{
1145 if (node->options & LOG_OPT_ESC) {
1146 if (start < stop) {
1147 stop--; /* reserve one byte for the final '\0' */
1148 while (start < stop && *string != '\0') {
1149 if (!FD_ISSET((unsigned char)(*string), map)) {
1150 if (!FD_ISSET((unsigned char)(*string), rfc5424_escape_map))
1151 *start++ = *string;
1152 else {
1153 if (start + 2 >= stop)
1154 break;
1155 *start++ = '\\';
1156 *start++ = *string;
1157 }
1158 }
1159 else {
1160 if (start + 3 >= stop)
1161 break;
1162 *start++ = escape;
1163 *start++ = hextab[(*string >> 4) & 15];
1164 *start++ = hextab[*string & 15];
1165 }
1166 string++;
1167 }
1168 *start = '\0';
1169 }
1170 }
1171 else {
1172 return encode_string(start, stop, escape, map, string);
1173 }
1174
1175 return start;
1176}
1177
1178/*
1179 * Encode the chunk.
1180 *
1181 * When using the +E log format option, it will try to escape '"\]'
1182 * characters with '\' as prefix. The same prefix should not be used as
1183 * <escape>.
1184 */
1185static char *lf_encode_chunk(char *start, char *stop,
1186 const char escape, const fd_set *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001187 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001188 struct logformat_node *node)
1189{
1190 char *str, *end;
1191
1192 if (node->options & LOG_OPT_ESC) {
1193 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001194 str = chunk->area;
1195 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001196
1197 stop--; /* reserve one byte for the final '\0' */
1198 while (start < stop && str < end) {
1199 if (!FD_ISSET((unsigned char)(*str), map)) {
1200 if (!FD_ISSET((unsigned char)(*str), rfc5424_escape_map))
1201 *start++ = *str;
1202 else {
1203 if (start + 2 >= stop)
1204 break;
1205 *start++ = '\\';
1206 *start++ = *str;
1207 }
1208 }
1209 else {
1210 if (start + 3 >= stop)
1211 break;
1212 *start++ = escape;
1213 *start++ = hextab[(*str >> 4) & 15];
1214 *start++ = hextab[*str & 15];
1215 }
1216 str++;
1217 }
1218 *start = '\0';
1219 }
1220 }
1221 else {
1222 return encode_chunk(start, stop, escape, map, chunk);
1223 }
1224
1225 return start;
1226}
1227
1228/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001229 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001230 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001231 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001232 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001233 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001234char *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 +01001235{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001236 if (size < 2)
1237 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001238
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001239 if (node->options & LOG_OPT_QUOTE) {
1240 *(dst++) = '"';
1241 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001242 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001243
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001244 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001245 if (++len > size)
1246 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001247 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001248 char *ret;
1249
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001250 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001251 if (ret == NULL || *ret != '\0')
1252 return NULL;
1253 len = ret - dst;
1254 }
1255 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001256 len = strlcpy2(dst, src, len);
1257 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001258
1259 size -= len;
1260 dst += len;
1261 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001262 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1263 if (size < 2)
1264 return NULL;
1265 *(dst++) = '-';
1266 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001267
1268 if (node->options & LOG_OPT_QUOTE) {
1269 if (size < 2)
1270 return NULL;
1271 *(dst++) = '"';
1272 }
1273
1274 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001275 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001276}
1277
Willy Tarreau26ffa852018-09-05 15:23:10 +02001278static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001279{
1280 return lf_text_len(dst, src, size, size, node);
1281}
1282
William Lallemand5f232402012-04-05 18:02:55 +02001283/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001284 * Write a IP address to the log string
William Lallemand5f232402012-04-05 18:02:55 +02001285 * +X option write in hexadecimal notation, most signifant byte on the left
1286 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001287char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001288{
1289 char *ret = dst;
1290 int iret;
1291 char pn[INET6_ADDRSTRLEN];
1292
1293 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001294 unsigned char *addr = NULL;
1295 switch (sockaddr->sa_family) {
1296 case AF_INET:
1297 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1298 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1299 break;
1300 case AF_INET6:
1301 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1302 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1303 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1304 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1305 break;
1306 default:
1307 return NULL;
1308 }
William Lallemand5f232402012-04-05 18:02:55 +02001309 if (iret < 0 || iret > size)
1310 return NULL;
1311 ret += iret;
1312 } else {
1313 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1314 ret = lf_text(dst, pn, size, node);
1315 if (ret == NULL)
1316 return NULL;
1317 }
1318 return ret;
1319}
1320
1321/*
1322 * Write a port to the log
1323 * +X option write in hexadecimal notation, most signifant byte on the left
1324 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001325char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001326{
1327 char *ret = dst;
1328 int iret;
1329
1330 if (node->options & LOG_OPT_HEXA) {
1331 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1332 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1333 if (iret < 0 || iret > size)
1334 return NULL;
1335 ret += iret;
1336 } else {
1337 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1338 if (ret == NULL)
1339 return NULL;
1340 }
1341 return ret;
1342}
1343
Dragan Dosen1322d092015-09-22 16:05:32 +02001344/* Re-generate time-based part of the syslog header in RFC3164 format at
1345 * the beginning of logheader once a second and return the pointer to the
1346 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001347 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001348static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001349{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001350 static THREAD_LOCAL long tvsec;
1351 static THREAD_LOCAL char *dataptr = NULL; /* backup of last end of header, NULL first time */
Willy Tarreau83061a82018-07-13 11:56:34 +02001352 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001353 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001354
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001355 if (unlikely(time != tvsec || dataptr == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001356 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001357 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001358 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001359
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001360 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001361 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001362
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001363 if (unlikely(global.log_send_hostname != host.area)) {
1364 host.area = global.log_send_hostname;
1365 host.data = host.area ? strlen(host.area) : 0;
1366 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001367 }
1368
Dragan Dosen59cee972015-09-19 22:09:02 +02001369 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001370 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001371 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001372 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001373 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001374 /* WARNING: depending upon implementations, snprintf may return
1375 * either -1 or the number of bytes that would be needed to store
1376 * the total message. In both cases, we must adjust it.
1377 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001378 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1379 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001380
Dragan Dosen59cee972015-09-19 22:09:02 +02001381 dataptr = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001382 }
1383
Willy Tarreau094af4e2015-01-07 15:03:42 +01001384 dataptr[0] = 0; // ensure we get rid of any previous attempt
1385
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001386 return dataptr;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001387}
1388
Dragan Dosen1322d092015-09-22 16:05:32 +02001389/* Re-generate time-based part of the syslog header in RFC5424 format at
1390 * the beginning of logheader_rfc5424 once a second and return the pointer
1391 * to the first character after it.
1392 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001393static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001394{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001395 static THREAD_LOCAL long tvsec;
1396 static THREAD_LOCAL char *dataptr = NULL; /* backup of last end of header, NULL first time */
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001397 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001398
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001399 if (unlikely(time != tvsec || dataptr == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001400 /* this string is rebuild only once a second */
1401 struct tm tm;
1402 int hdr_len;
1403
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001404 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001405 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001406 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001407
1408 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001409 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001410 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001411 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001412 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001413 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001414 /* WARNING: depending upon implementations, snprintf may return
1415 * either -1 or the number of bytes that would be needed to store
1416 * the total message. In both cases, we must adjust it.
1417 */
1418 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1419 hdr_len = global.max_syslog_len;
1420
1421 dataptr = logheader_rfc5424 + hdr_len;
1422 }
1423
1424 dataptr[0] = 0; // ensure we get rid of any previous attempt
1425
1426 return dataptr;
1427}
1428
William Lallemand2a4a44f2012-02-06 16:00:33 +01001429/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001430 * This function sends the syslog message using a printf format string. It
1431 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001432 */
1433void send_log(struct proxy *p, int level, const char *format, ...)
1434{
1435 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001436 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001437
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001438 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001439 return;
1440
William Lallemand2a4a44f2012-02-06 16:00:33 +01001441 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001442 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001443 if (data_len < 0 || data_len > global.max_syslog_len)
1444 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001445 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001446
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001447 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001448}
1449
1450/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001451 * This function sends a syslog message to <logsrv>.
1452 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1453 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1454 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001455 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001456 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001457 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001458static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1459 int level, char *message, size_t size, char *sd, size_t sd_size,
1460 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001461{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001462 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1463 static THREAD_LOCAL struct msghdr msghdr = {
1464 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001465 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1466 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001467 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1468 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1469 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001470 time_t time = date.tv_sec;
Dragan Dosen1322d092015-09-22 16:05:32 +02001471 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001472 size_t hdr_size;
Willy Tarreau83061a82018-07-13 11:56:34 +02001473 struct buffer *tag = &global.log_tag;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001474 int fac_level;
1475 int *plogfd;
1476 char *pid_sep1 = "", *pid_sep2 = "";
1477 char logheader_short[3];
1478 int sent;
1479 int maxlen;
1480 int hdr_max = 0;
1481 int tag_max = 0;
1482 int pid_sep1_max = 0;
1483 int pid_sep2_max = 0;
1484 int sd_max = 0;
1485 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001486
1487 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001488
1489 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001490
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001491 if (logsrv->addr.ss_family == AF_UNSPEC) {
1492 /* the socket's address is a file descriptor */
1493 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1494 if (unlikely(!((struct sockaddr_in *)&logsrv->addr)->sin_port)) {
1495 /* FD not yet initialized to non-blocking mode.
1496 * DON'T DO IT ON A TERMINAL!
1497 */
1498 if (!isatty(*plogfd))
1499 fcntl(*plogfd, F_SETFL, O_NONBLOCK);
1500 ((struct sockaddr_in *)&logsrv->addr)->sin_port = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001501 }
Robert Tsai81ae1952007-12-05 10:47:29 +01001502 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001503 else if (logsrv->addr.ss_family == AF_UNIX)
1504 plogfd = &logfdunix;
1505 else
1506 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001507
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001508 if (unlikely(*plogfd < 0)) {
1509 /* socket not successfully initialized yet */
1510 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1511 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1512 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001513
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001514 if (!once) {
1515 once = 1; /* note: no need for atomic ops here */
1516 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1517 nblogger, strerror(errno), errno);
1518 }
1519 return;
1520 } else {
1521 /* we don't want to receive anything on this socket */
1522 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1523 /* does nothing under Linux, maybe needed for others */
1524 shutdown(*plogfd, SHUT_RD);
1525 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1526 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001527 }
1528
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001529 switch (logsrv->format) {
1530 case LOG_FORMAT_RFC3164:
1531 hdr = logheader;
1532 hdr_ptr = update_log_hdr(time);
1533 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001534
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001535 case LOG_FORMAT_RFC5424:
1536 hdr = logheader_rfc5424;
1537 hdr_ptr = update_log_hdr_rfc5424(time);
1538 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1539 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001540
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001541 case LOG_FORMAT_SHORT:
1542 /* all fields are known, skip the header generation */
1543 hdr = logheader_short;
1544 hdr[0] = '<';
1545 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1546 hdr[2] = '>';
1547 hdr_ptr = hdr;
1548 hdr_max = 3;
1549 maxlen = logsrv->maxlen - hdr_max;
1550 max = MIN(size, maxlen) - 1;
1551 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001552
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001553 case LOG_FORMAT_RAW:
1554 /* all fields are known, skip the header generation */
1555 hdr_ptr = hdr = "";
1556 hdr_max = 0;
1557 maxlen = logsrv->maxlen;
1558 max = MIN(size, maxlen) - 1;
1559 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001560
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001561 default:
1562 return; /* must never happen */
1563 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001564
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001565 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001566
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001567 /* For each target, we may have a different facility.
1568 * We can also have a different log level for each message.
1569 * This induces variations in the message header length.
1570 * Since we don't want to recompute it each time, nor copy it every
1571 * time, we only change the facility in the pre-computed header,
1572 * and we change the pointer to the header accordingly.
1573 */
1574 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1575 hdr_ptr = hdr + 3; /* last digit of the log level */
1576 do {
1577 *hdr_ptr = '0' + fac_level % 10;
1578 fac_level /= 10;
1579 hdr_ptr--;
1580 } while (fac_level && hdr_ptr > hdr);
1581 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001582
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001583 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001584
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001585 /* time-based header */
1586 if (unlikely(hdr_size >= logsrv->maxlen)) {
1587 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1588 sd_max = 0;
1589 goto send;
1590 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001591
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001592 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001593
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001594 /* tag */
1595 tag_max = tag->data;
1596 if (unlikely(tag_max >= maxlen)) {
1597 tag_max = maxlen - 1;
1598 sd_max = 0;
1599 goto send;
1600 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001601
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001602 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001603
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001604 /* first pid separator */
1605 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1606 if (unlikely(pid_sep1_max >= maxlen)) {
1607 pid_sep1_max = maxlen - 1;
1608 sd_max = 0;
1609 goto send;
1610 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001611
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001612 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1613 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001614
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001615 /* pid */
1616 if (unlikely(pid_size >= maxlen)) {
1617 pid_size = maxlen - 1;
1618 sd_max = 0;
1619 goto send;
1620 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001621
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001622 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001623
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001624 /* second pid separator */
1625 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1626 if (unlikely(pid_sep2_max >= maxlen)) {
1627 pid_sep2_max = maxlen - 1;
1628 sd_max = 0;
1629 goto send;
1630 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001631
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001632 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1633 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001634
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001635 /* structured-data */
1636 if (sd_max >= maxlen) {
1637 sd_max = maxlen - 1;
1638 goto send;
1639 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001640
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001641 max = MIN(size, maxlen - sd_max) - 1;
1642send:
1643 iovec[0].iov_base = hdr_ptr;
1644 iovec[0].iov_len = hdr_max;
1645 iovec[1].iov_base = tag_str;
1646 iovec[1].iov_len = tag_size;
1647 iovec[2].iov_base = pid_sep1;
1648 iovec[2].iov_len = pid_sep1_max;
1649 iovec[3].iov_base = pid_str;
1650 iovec[3].iov_len = pid_size;
1651 iovec[4].iov_base = pid_sep2;
1652 iovec[4].iov_len = pid_sep2_max;
1653 iovec[5].iov_base = sd;
1654 iovec[5].iov_len = sd_max;
1655 iovec[6].iov_base = dataptr;
1656 iovec[6].iov_len = max;
1657 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1658 iovec[7].iov_len = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001659
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001660 if (logsrv->addr.ss_family == AF_UNSPEC) {
1661 /* the target is a direct file descriptor */
1662 sent = writev(*plogfd, iovec, 8);
1663 }
1664 else {
1665 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1666 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001667
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001668 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1669 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001670
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001671 if (sent < 0) {
1672 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001673
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001674 if (errno == EAGAIN)
1675 _HA_ATOMIC_ADD(&dropped_logs, 1);
1676 else if (!once) {
1677 once = 1; /* note: no need for atomic ops here */
1678 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1679 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001680 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001681 }
1682}
Dragan Dosen59cee972015-09-19 22:09:02 +02001683
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001684/*
1685 * This function sends a syslog message.
1686 * It doesn't care about errors nor does it report them.
1687 * The arguments <sd> and <sd_size> are used for the structured-data part
1688 * in RFC5424 formatted syslog messages.
1689 */
1690void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
1691{
1692 struct list *logsrvs = NULL;
1693 struct logsrv *logsrv;
1694 int nblogger;
1695 static THREAD_LOCAL int curr_pid;
1696 static THREAD_LOCAL char pidstr[100];
1697 static THREAD_LOCAL struct buffer pid;
1698 struct buffer *tag = &global.log_tag;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001699
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001700 if (p == NULL) {
1701 if (!LIST_ISEMPTY(&global.logsrvs)) {
1702 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001703 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001704 } else {
1705 if (!LIST_ISEMPTY(&p->logsrvs)) {
1706 logsrvs = &p->logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001707 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001708 if (p->log_tag.area) {
1709 tag = &p->log_tag;
1710 }
1711 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001712
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001713 if (!logsrvs)
1714 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001715
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001716 if (unlikely(curr_pid != getpid())) {
1717 curr_pid = getpid();
1718 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1719 chunk_initstr(&pid, pidstr);
1720 }
1721
1722 /* Send log messages to syslog server. */
1723 nblogger = 0;
1724 list_for_each_entry(logsrv, logsrvs, list) {
1725 /* we can filter the level of the messages that are sent to each logger */
1726 if (level > logsrv->level)
1727 continue;
1728
1729 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1730 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001731 }
1732}
1733
William Lallemandbddd4fd2012-02-27 11:23:10 +01001734extern fd_set hdr_encode_map[];
1735extern fd_set url_encode_map[];
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001736extern fd_set http_encode_map[];
William Lallemandbddd4fd2012-02-27 11:23:10 +01001737
Willy Tarreaubaaee002006-06-26 02:48:02 +02001738
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001739const 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 +01001740const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1741 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1742 Set-cookie Updated, unknown, unknown */
1743
William Lallemand1d705562012-03-12 12:46:41 +01001744/*
1745 * try to write a character if there is enough space, or goto out
1746 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001747#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001748 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001749 *(tmplog++) = (x); \
1750 } else { \
1751 goto out; \
1752 } \
1753 } while(0)
1754
Dragan Dosen835b9212016-02-12 13:23:03 +01001755
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001756/* Initializes some log data at boot */
1757static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001758{
1759 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001760 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001761
1762 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1763 * inside PARAM-VALUE should be escaped with '\' as prefix.
1764 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1765 * details.
1766 */
1767 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1768
1769 tmp = "\"\\]";
1770 while (*tmp) {
1771 FD_SET(*tmp, rfc5424_escape_map);
1772 tmp++;
1773 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001774
1775 /* initialize the log header encoding map : '{|}"#' should be encoded with
1776 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1777 * URL encoding only requires '"', '#' to be encoded as well as non-
1778 * printable characters above.
1779 */
1780 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1781 memset(url_encode_map, 0, sizeof(url_encode_map));
1782 for (i = 0; i < 32; i++) {
1783 FD_SET(i, hdr_encode_map);
1784 FD_SET(i, url_encode_map);
1785 }
1786 for (i = 127; i < 256; i++) {
1787 FD_SET(i, hdr_encode_map);
1788 FD_SET(i, url_encode_map);
1789 }
1790
1791 tmp = "\"#{|}";
1792 while (*tmp) {
1793 FD_SET(*tmp, hdr_encode_map);
1794 tmp++;
1795 }
1796
1797 tmp = "\"#";
1798 while (*tmp) {
1799 FD_SET(*tmp, url_encode_map);
1800 tmp++;
1801 }
1802
1803 /* initialize the http header encoding map. The draft httpbis define the
1804 * header content as:
1805 *
1806 * HTTP-message = start-line
1807 * *( header-field CRLF )
1808 * CRLF
1809 * [ message-body ]
1810 * header-field = field-name ":" OWS field-value OWS
1811 * field-value = *( field-content / obs-fold )
1812 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1813 * obs-fold = CRLF 1*( SP / HTAB )
1814 * field-vchar = VCHAR / obs-text
1815 * VCHAR = %x21-7E
1816 * obs-text = %x80-FF
1817 *
1818 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1819 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001820 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001821 */
1822 memset(http_encode_map, 0, sizeof(http_encode_map));
1823 for (i = 0x00; i <= 0x08; i++)
1824 FD_SET(i, http_encode_map);
1825 for (i = 0x0a; i <= 0x1f; i++)
1826 FD_SET(i, http_encode_map);
1827 FD_SET(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001828}
William Lallemand1d705562012-03-12 12:46:41 +01001829
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001830INITCALL0(STG_PREPARE, init_log);
1831
Christopher Fauletcd7879a2017-10-27 13:53:47 +02001832static int init_log_buffers_per_thread()
1833{
1834 return init_log_buffers();
1835}
1836
1837static void deinit_log_buffers_per_thread()
1838{
1839 deinit_log_buffers();
1840}
1841
Christopher Faulet0132d062017-07-26 15:33:35 +02001842/* Initialize log buffers used for syslog messages */
1843int init_log_buffers()
1844{
1845 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
1846 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
1847 logline = my_realloc2(logline, global.max_syslog_len + 1);
1848 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1849 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1850 return 0;
1851 return 1;
1852}
1853
1854/* Deinitialize log buffers used for syslog messages */
1855void deinit_log_buffers()
1856{
Olivier Houchard7c497112019-03-07 14:19:24 +01001857 void *tmp_startup_logs;
1858
Christopher Faulet0132d062017-07-26 15:33:35 +02001859 free(logheader);
1860 free(logheader_rfc5424);
1861 free(logline);
1862 free(logline_rfc5424);
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001863 tmp_startup_logs = _HA_ATOMIC_XCHG(&startup_logs, NULL);
Olivier Houchard7c497112019-03-07 14:19:24 +01001864 free(tmp_startup_logs);
1865
Christopher Faulet0132d062017-07-26 15:33:35 +02001866 logheader = NULL;
1867 logheader_rfc5424 = NULL;
1868 logline = NULL;
1869 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001870 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001871}
1872
Willy Tarreaudf974472012-12-28 02:44:01 +01001873/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1874 * <maxsize> characters. Returns the size of the output string in characters,
1875 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001876 * is not zero. It requires a valid session and optionally a stream. If the
1877 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001878 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001879int 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 +02001880{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001881 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001882 struct proxy *be;
1883 struct http_txn *txn;
1884 const struct strm_logs *logs;
1885 const struct connection *be_conn;
1886 unsigned int s_flags;
1887 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001888 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001889 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001890 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001891 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001892 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001893 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001894 int t_request;
1895 int hdr;
1896 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001897 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001898 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001899 char *ret;
1900 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001901 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001902 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001903 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001904
William Lallemandbddd4fd2012-02-27 11:23:10 +01001905 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001906
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001907 if (likely(s)) {
1908 be = s->be;
1909 txn = s->txn;
1910 be_conn = cs_conn(objt_cs(s->si[1].end));
1911 s_flags = s->flags;
1912 uniq_id = s->uniq_id;
1913 logs = &s->logs;
1914 } else {
1915 /* we have no stream so we first need to initialize a few
1916 * things that are needed later. We do increment the request
1917 * ID so that it's uniquely assigned to this request just as
1918 * if the request had reached the point of being processed.
1919 * A request error is reported as it's the only element we have
1920 * here and which justifies emitting such a log.
1921 */
1922 be = fe;
1923 txn = NULL;
1924 be_conn = NULL;
1925 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001926 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001927
1928 /* prepare a valid log structure */
1929 tmp_strm_log.tv_accept = sess->tv_accept;
1930 tmp_strm_log.accept_date = sess->accept_date;
1931 tmp_strm_log.t_handshake = sess->t_handshake;
1932 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1933 tv_zero(&tmp_strm_log.tv_request);
1934 tmp_strm_log.t_queue = -1;
1935 tmp_strm_log.t_connect = -1;
1936 tmp_strm_log.t_data = -1;
1937 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1938 tmp_strm_log.bytes_in = 0;
1939 tmp_strm_log.bytes_out = 0;
1940 tmp_strm_log.prx_queue_pos = 0;
1941 tmp_strm_log.srv_queue_pos = 0;
1942
1943 logs = &tmp_strm_log;
1944 }
1945
William Lallemandbddd4fd2012-02-27 11:23:10 +01001946 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001947 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1948 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001949
William Lallemand1d705562012-03-12 12:46:41 +01001950 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001951
William Lallemandbddd4fd2012-02-27 11:23:10 +01001952 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001953 if (LIST_ISEMPTY(list_format))
1954 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001955
William Lallemand1d705562012-03-12 12:46:41 +01001956 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001957 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001958 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001959 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001960 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001961
Willy Tarreauc8368452012-12-21 00:09:23 +01001962 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01001963 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01001964 if (!last_isspace) {
1965 LOGCHAR(' ');
1966 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001967 }
1968 break;
1969
William Lallemand1d705562012-03-12 12:46:41 +01001970 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01001971 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02001972 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001973 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001974 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001975 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001976 last_isspace = 0;
1977 break;
1978
Willy Tarreauc8368452012-12-21 00:09:23 +01001979 case LOG_FMT_EXPR: // sample expression, may be request or response
1980 key = NULL;
Olivier Houchardf90db442018-12-15 14:00:06 +01001981 if (tmp->options & LOG_OPT_REQ_CAP && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02001982 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 +01001983 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02001984 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 +01001985 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01001986 ret = lf_encode_chunk(tmplog, dst + maxsize,
1987 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001988 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001989 ret = lf_text_len(tmplog,
1990 key ? key->data.u.str.area : NULL,
1991 key ? key->data.u.str.data : 0,
1992 dst + maxsize - tmplog,
1993 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01001994 if (ret == 0)
1995 goto out;
1996 tmplog = ret;
1997 last_isspace = 0;
1998 break;
1999
Willy Tarreau2beef582012-12-20 17:22:52 +01002000 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002001 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002002 if (conn)
2003 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.from, dst + maxsize - tmplog, tmp);
2004 else
2005 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002006 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002007 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002008 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002009 last_isspace = 0;
2010 break;
2011
Willy Tarreau2beef582012-12-20 17:22:52 +01002012 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002013 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002014 if (conn) {
2015 if (conn->addr.from.ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002016 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002017 } else {
2018 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.from,
2019 dst + maxsize - tmplog, tmp);
2020 }
William Lallemand5f232402012-04-05 18:02:55 +02002021 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002022 else
2023 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2024
William Lallemand5f232402012-04-05 18:02:55 +02002025 if (ret == NULL)
2026 goto out;
2027 tmplog = ret;
2028 last_isspace = 0;
2029 break;
2030
Willy Tarreau2beef582012-12-20 17:22:52 +01002031 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002032 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002033 if (conn) {
2034 conn_get_to_addr(conn);
2035 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
2036 }
2037 else
2038 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2039
William Lallemand1d705562012-03-12 12:46:41 +01002040 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002041 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002042 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002043 last_isspace = 0;
2044 break;
2045
Willy Tarreau2beef582012-12-20 17:22:52 +01002046 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002047 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002048 if (conn) {
2049 conn_get_to_addr(conn);
2050 if (conn->addr.to.ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002051 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002052 else
2053 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002054 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002055 else
2056 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2057
William Lallemand5f232402012-04-05 18:02:55 +02002058 if (ret == NULL)
2059 goto out;
2060 tmplog = ret;
2061 last_isspace = 0;
2062 break;
2063
Willy Tarreau2beef582012-12-20 17:22:52 +01002064 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002065 if (be_conn)
2066 ret = lf_ip(tmplog, (const struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002067 else
2068 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2069
William Lallemand1d705562012-03-12 12:46:41 +01002070 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002071 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002072 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002073 last_isspace = 0;
2074 break;
2075
Willy Tarreau2beef582012-12-20 17:22:52 +01002076 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002077 if (be_conn)
2078 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002079 else
2080 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2081
William Lallemand5f232402012-04-05 18:02:55 +02002082 if (ret == NULL)
2083 goto out;
2084 tmplog = ret;
2085 last_isspace = 0;
2086 break;
2087
Willy Tarreau2beef582012-12-20 17:22:52 +01002088 case LOG_FMT_SERVERIP: // %si
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002089 if (be_conn)
2090 ret = lf_ip(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002091 else
2092 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2093
William Lallemand5f232402012-04-05 18:02:55 +02002094 if (ret == NULL)
2095 goto out;
2096 tmplog = ret;
2097 last_isspace = 0;
2098 break;
2099
Willy Tarreau2beef582012-12-20 17:22:52 +01002100 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002101 if (be_conn)
2102 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002103 else
2104 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2105
William Lallemand1d705562012-03-12 12:46:41 +01002106 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002107 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002108 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002109 last_isspace = 0;
2110 break;
2111
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002112 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002113 get_localtime(logs->accept_date.tv_sec, &tm);
2114 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002115 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002116 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002117 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002118 last_isspace = 0;
2119 break;
2120
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002121 case LOG_FMT_tr: // %tr = start of request date
2122 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002123 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 +02002124 get_localtime(tv.tv_sec, &tm);
2125 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2126 if (ret == NULL)
2127 goto out;
2128 tmplog = ret;
2129 last_isspace = 0;
2130 break;
2131
2132 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002133 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002134 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002135 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002136 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002137 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002138 last_isspace = 0;
2139 break;
2140
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002141 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002142 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 +02002143 get_gmtime(tv.tv_sec, &tm);
2144 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2145 if (ret == NULL)
2146 goto out;
2147 tmplog = ret;
2148 last_isspace = 0;
2149 break;
2150
2151 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002152 get_localtime(logs->accept_date.tv_sec, &tm);
2153 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002154 if (ret == NULL)
2155 goto out;
2156 tmplog = ret;
2157 last_isspace = 0;
2158 break;
2159
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002160 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002161 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 +02002162 get_localtime(tv.tv_sec, &tm);
2163 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2164 if (ret == NULL)
2165 goto out;
2166 tmplog = ret;
2167 last_isspace = 0;
2168 break;
2169
William Lallemand5f232402012-04-05 18:02:55 +02002170 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002171 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002172 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002173 if (iret < 0 || iret > dst + maxsize - tmplog)
2174 goto out;
2175 last_isspace = 0;
2176 tmplog += iret;
2177 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002178 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002179 if (ret == NULL)
2180 goto out;
2181 tmplog = ret;
2182 last_isspace = 0;
2183 }
2184 break;
2185
William Lallemand1d705562012-03-12 12:46:41 +01002186 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002187 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002188 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002189 if (iret < 0 || iret > dst + maxsize - tmplog)
2190 goto out;
2191 last_isspace = 0;
2192 tmplog += iret;
2193 } else {
2194 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002195 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002196 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002197 tmplog, 4);
2198 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002199 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002200 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002201 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002202 }
2203 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002204
William Lallemand1d705562012-03-12 12:46:41 +01002205 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002206 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002207 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002208 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002209 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002210 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002211 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002212 break;
2213
Willy Tarreau773d65f2012-10-12 14:56:11 +02002214 case LOG_FMT_FRONTEND_XPRT: // %ft
2215 src = fe->id;
2216 if (tmp->options & LOG_OPT_QUOTE)
2217 LOGCHAR('"');
2218 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2219 if (iret == 0)
2220 goto out;
2221 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002222 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002223 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002224 if (tmp->options & LOG_OPT_QUOTE)
2225 LOGCHAR('"');
2226 last_isspace = 0;
2227 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002228#ifdef USE_OPENSSL
2229 case LOG_FMT_SSL_CIPHER: // %sslc
2230 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002231 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002232 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002233 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002234 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002235 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2236 if (ret == NULL)
2237 goto out;
2238 tmplog = ret;
2239 last_isspace = 0;
2240 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002241
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002242 case LOG_FMT_SSL_VERSION: // %sslv
2243 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002244 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002245 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002246 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002247 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002248 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2249 if (ret == NULL)
2250 goto out;
2251 tmplog = ret;
2252 last_isspace = 0;
2253 break;
2254#endif
William Lallemand1d705562012-03-12 12:46:41 +01002255 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002256 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002257 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002258 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002259 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002260 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002261 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002262 break;
2263
William Lallemand1d705562012-03-12 12:46:41 +01002264 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002265 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002266 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002267 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002268 break;
2269 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002270 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002271 break;
2272 default:
2273 src = "<NOSRV>";
2274 break;
2275 }
William Lallemand5f232402012-04-05 18:02:55 +02002276 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002277 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002278 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002279 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002280 last_isspace = 0;
2281 break;
2282
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002283 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002284 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002285 if (ret == NULL)
2286 goto out;
2287 tmplog = ret;
2288 last_isspace = 0;
2289 break;
2290
2291 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002292 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002293 if (ret == NULL)
2294 goto out;
2295 tmplog = ret;
2296 last_isspace = 0;
2297 break;
2298
2299 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002300 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002301 tmplog, dst + maxsize - tmplog);
2302 if (ret == NULL)
2303 goto out;
2304 tmplog = ret;
2305 last_isspace = 0;
2306 break;
2307
2308 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002309 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002310 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002311 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002312 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002313 last_isspace = 0;
2314 break;
2315
William Lallemand1d705562012-03-12 12:46:41 +01002316 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002317 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002318 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002319 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002320 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002321 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002322 last_isspace = 0;
2323 break;
2324
William Lallemand1d705562012-03-12 12:46:41 +01002325 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002326 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002327 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002328 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002329 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002330 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002331 last_isspace = 0;
2332 break;
2333
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002334 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002335 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002336 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
Willy Tarreau27b639d2016-05-17 17:55:27 +02002343 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002344 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002345 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002346 tmplog, dst + maxsize - tmplog);
2347 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002348 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002349 tmplog, dst + maxsize - tmplog);
2350 if (ret == NULL)
2351 goto out;
2352 tmplog = ret;
2353 last_isspace = 0;
2354 break;
2355
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002356 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2357 if (!(fe->to_log & LW_BYTES))
2358 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002359 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 +02002360 tmplog, dst + maxsize - tmplog);
2361 if (ret == NULL)
2362 goto out;
2363 tmplog = ret;
2364 last_isspace = 0;
2365 break;
2366
2367 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002368 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002369 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002370 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002371 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002372 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002373 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002374 last_isspace = 0;
2375 break;
2376
Willy Tarreau2beef582012-12-20 17:22:52 +01002377 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002378 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002379 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002380 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002381 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002382 last_isspace = 0;
2383 break;
2384
William Lallemand1d705562012-03-12 12:46:41 +01002385 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002386 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002387 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002388 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002389 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002390 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002391 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002392 last_isspace = 0;
2393 break;
2394
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002395 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002396 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002397 if (ret == NULL)
2398 goto out;
2399 tmplog = ret;
2400 last_isspace = 0;
2401 break;
2402
Willy Tarreau2beef582012-12-20 17:22:52 +01002403 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002404 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002405 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002406 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002407 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
Willy Tarreau2beef582012-12-20 17:22:52 +01002412 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002413 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002414 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002415 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002416 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002417 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002418 last_isspace = 0;
2419 break;
2420
William Lallemand1d705562012-03-12 12:46:41 +01002421 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002422 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2423 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002424 *tmplog = '\0';
2425 last_isspace = 0;
2426 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002427
William Lallemand1d705562012-03-12 12:46:41 +01002428 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002429 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2430 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002431 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2432 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 +01002433 last_isspace = 0;
2434 break;
2435
William Lallemand1d705562012-03-12 12:46:41 +01002436 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002437 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002438 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002439 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002440 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002441 last_isspace = 0;
2442 break;
2443
William Lallemand1d705562012-03-12 12:46:41 +01002444 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002445 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002446 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002447 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002448 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002449 last_isspace = 0;
2450 break;
2451
William Lallemand1d705562012-03-12 12:46:41 +01002452 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002453 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002454 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002455 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002456 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002457 last_isspace = 0;
2458 break;
2459
William Lallemand1d705562012-03-12 12:46:41 +01002460 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002461 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002462 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002463 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002464 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002465 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002466 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002467 last_isspace = 0;
2468 break;
2469
William Lallemand1d705562012-03-12 12:46:41 +01002470 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002471 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002472 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002473 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002474 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002475 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002476 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002477 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002478 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002479 last_isspace = 0;
2480 break;
2481
William Lallemand1d705562012-03-12 12:46:41 +01002482 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002483 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002484 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002485 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002486 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002487 last_isspace = 0;
2488 break;
2489
William Lallemand1d705562012-03-12 12:46:41 +01002490 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002491 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002492 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002493 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002494 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002495 last_isspace = 0;
2496 break;
2497
William Lallemand1d705562012-03-12 12:46:41 +01002498 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002499 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002500 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002501 if (tmp->options & LOG_OPT_QUOTE)
2502 LOGCHAR('"');
2503 LOGCHAR('{');
2504 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2505 if (hdr)
2506 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002507 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002508 ret = lf_encode_string(tmplog, dst + maxsize,
2509 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002510 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002511 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002512 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002513 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002514 }
2515 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002516 if (tmp->options & LOG_OPT_QUOTE)
2517 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002518 last_isspace = 0;
2519 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002520 break;
2521
William Lallemand1d705562012-03-12 12:46:41 +01002522 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002523 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002524 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002525 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2526 if (hdr > 0)
2527 LOGCHAR(' ');
2528 if (tmp->options & LOG_OPT_QUOTE)
2529 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002530 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002531 ret = lf_encode_string(tmplog, dst + maxsize,
2532 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002533 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002534 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002535 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002536 } else if (!(tmp->options & LOG_OPT_QUOTE))
2537 LOGCHAR('-');
2538 if (tmp->options & LOG_OPT_QUOTE)
2539 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002540 last_isspace = 0;
2541 }
2542 }
2543 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002544
William Lallemand1d705562012-03-12 12:46:41 +01002545
2546 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002547 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002548 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002549 if (tmp->options & LOG_OPT_QUOTE)
2550 LOGCHAR('"');
2551 LOGCHAR('{');
2552 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2553 if (hdr)
2554 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002555 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002556 ret = lf_encode_string(tmplog, dst + maxsize,
2557 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002558 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002559 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002560 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002561 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002562 }
2563 LOGCHAR('}');
2564 last_isspace = 0;
2565 if (tmp->options & LOG_OPT_QUOTE)
2566 LOGCHAR('"');
2567 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002568 break;
2569
William Lallemand1d705562012-03-12 12:46:41 +01002570 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002571 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002572 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002573 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2574 if (hdr > 0)
2575 LOGCHAR(' ');
2576 if (tmp->options & LOG_OPT_QUOTE)
2577 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002578 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002579 ret = lf_encode_string(tmplog, dst + maxsize,
2580 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002581 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002582 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002583 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002584 } else if (!(tmp->options & LOG_OPT_QUOTE))
2585 LOGCHAR('-');
2586 if (tmp->options & LOG_OPT_QUOTE)
2587 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002588 last_isspace = 0;
2589 }
2590 }
2591 break;
2592
William Lallemand1d705562012-03-12 12:46:41 +01002593 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002594 /* Request */
2595 if (tmp->options & LOG_OPT_QUOTE)
2596 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002597 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002598 ret = lf_encode_string(tmplog, dst + maxsize,
2599 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002600 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002601 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002602 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002603 if (tmp->options & LOG_OPT_QUOTE)
2604 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002605 last_isspace = 0;
2606 break;
William Lallemand5f232402012-04-05 18:02:55 +02002607
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002608 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002609 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002610
Willy Tarreaub7636d12015-06-17 19:58:02 +02002611 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002612 LOGCHAR('"');
2613
2614 end = uri + strlen(uri);
2615 // look for the first whitespace character
2616 while (uri < end && !HTTP_IS_SPHT(*uri))
2617 uri++;
2618
2619 // keep advancing past multiple spaces
2620 while (uri < end && HTTP_IS_SPHT(*uri)) {
2621 uri++; nspaces++;
2622 }
2623
2624 // look for first space or question mark after url
2625 spc = uri;
2626 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2627 spc++;
2628
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002629 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002630 chunk.area = "<BADREQ>";
2631 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002632 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002633 chunk.area = uri;
2634 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002635 }
2636
Dragan Dosen835b9212016-02-12 13:23:03 +01002637 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002638 if (ret == NULL || *ret != '\0')
2639 goto out;
2640
2641 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002642 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002643 LOGCHAR('"');
2644
2645 last_isspace = 0;
2646 break;
2647
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002648 case LOG_FMT_HTTP_QUERY: // %HQ
2649 if (tmp->options & LOG_OPT_QUOTE)
2650 LOGCHAR('"');
2651
Willy Tarreau57bc8912016-04-25 17:09:40 +02002652 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002653 chunk.area = "<BADREQ>";
2654 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002655 } else {
2656 uri = txn->uri;
2657 end = uri + strlen(uri);
2658 // look for the first question mark
2659 while (uri < end && *uri != '?')
2660 uri++;
2661
2662 qmark = uri;
2663 // look for first space or question mark after url
2664 while (uri < end && !HTTP_IS_SPHT(*uri))
2665 uri++;
2666
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002667 chunk.area = qmark;
2668 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002669 }
2670
Dragan Dosen835b9212016-02-12 13:23:03 +01002671 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002672 if (ret == NULL || *ret != '\0')
2673 goto out;
2674
2675 tmplog = ret;
2676 if (tmp->options & LOG_OPT_QUOTE)
2677 LOGCHAR('"');
2678
2679 last_isspace = 0;
2680 break;
2681
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002682 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002683 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002684
Willy Tarreaub7636d12015-06-17 19:58:02 +02002685 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002686 LOGCHAR('"');
2687
2688 end = uri + strlen(uri);
2689 // look for the first whitespace character
2690 while (uri < end && !HTTP_IS_SPHT(*uri))
2691 uri++;
2692
2693 // keep advancing past multiple spaces
2694 while (uri < end && HTTP_IS_SPHT(*uri)) {
2695 uri++; nspaces++;
2696 }
2697
2698 // look for first space after url
2699 spc = uri;
2700 while (spc < end && !HTTP_IS_SPHT(*spc))
2701 spc++;
2702
Willy Tarreau57bc8912016-04-25 17:09:40 +02002703 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002704 chunk.area = "<BADREQ>";
2705 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002706 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002707 chunk.area = uri;
2708 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002709 }
2710
Dragan Dosen835b9212016-02-12 13:23:03 +01002711 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002712 if (ret == NULL || *ret != '\0')
2713 goto out;
2714
2715 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002716 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002717 LOGCHAR('"');
2718
2719 last_isspace = 0;
2720 break;
2721
2722 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002723 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002724 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002725 LOGCHAR('"');
2726
2727 end = uri + strlen(uri);
2728 // look for the first whitespace character
2729 spc = uri;
2730 while (spc < end && !HTTP_IS_SPHT(*spc))
2731 spc++;
2732
2733 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002734 chunk.area = "<BADREQ>";
2735 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002736 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002737 chunk.area = uri;
2738 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002739 }
2740
Dragan Dosen835b9212016-02-12 13:23:03 +01002741 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002742 if (ret == NULL || *ret != '\0')
2743 goto out;
2744
2745 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002746 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002747 LOGCHAR('"');
2748
2749 last_isspace = 0;
2750 break;
2751
2752 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002753 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002754 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002755 LOGCHAR('"');
2756
2757 end = uri + strlen(uri);
2758 // look for the first whitespace character
2759 while (uri < end && !HTTP_IS_SPHT(*uri))
2760 uri++;
2761
2762 // keep advancing past multiple spaces
2763 while (uri < end && HTTP_IS_SPHT(*uri)) {
2764 uri++; nspaces++;
2765 }
2766
2767 // look for the next whitespace character
2768 while (uri < end && !HTTP_IS_SPHT(*uri))
2769 uri++;
2770
2771 // keep advancing past multiple spaces
2772 while (uri < end && HTTP_IS_SPHT(*uri))
2773 uri++;
2774
Willy Tarreau57bc8912016-04-25 17:09:40 +02002775 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002776 chunk.area = "<BADREQ>";
2777 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002778 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002779 chunk.area = "HTTP/0.9";
2780 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002781 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002782 chunk.area = uri;
2783 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002784 }
2785
Dragan Dosen835b9212016-02-12 13:23:03 +01002786 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002787 if (ret == NULL || *ret != '\0')
2788 goto out;
2789
2790 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002791 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002792 LOGCHAR('"');
2793
2794 last_isspace = 0;
2795 break;
2796
William Lallemand5f232402012-04-05 18:02:55 +02002797 case LOG_FMT_COUNTER: // %rt
2798 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002799 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002800 if (iret < 0 || iret > dst + maxsize - tmplog)
2801 goto out;
2802 last_isspace = 0;
2803 tmplog += iret;
2804 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002805 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002806 if (ret == NULL)
2807 goto out;
2808 tmplog = ret;
2809 last_isspace = 0;
2810 }
2811 break;
2812
Willy Tarreau7346acb2014-08-28 15:03:15 +02002813 case LOG_FMT_LOGCNT: // %lc
2814 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002815 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002816 if (iret < 0 || iret > dst + maxsize - tmplog)
2817 goto out;
2818 last_isspace = 0;
2819 tmplog += iret;
2820 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002821 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002822 if (ret == NULL)
2823 goto out;
2824 tmplog = ret;
2825 last_isspace = 0;
2826 }
2827 break;
2828
William Lallemand5f232402012-04-05 18:02:55 +02002829 case LOG_FMT_HOSTNAME: // %H
2830 src = hostname;
2831 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2832 if (ret == NULL)
2833 goto out;
2834 tmplog = ret;
2835 last_isspace = 0;
2836 break;
2837
2838 case LOG_FMT_PID: // %pid
2839 if (tmp->options & LOG_OPT_HEXA) {
2840 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2841 if (iret < 0 || iret > dst + maxsize - tmplog)
2842 goto out;
2843 last_isspace = 0;
2844 tmplog += iret;
2845 } else {
2846 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2847 if (ret == NULL)
2848 goto out;
2849 tmplog = ret;
2850 last_isspace = 0;
2851 }
2852 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002853
2854 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002855 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002856 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002857 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002858 if (ret == NULL)
2859 goto out;
2860 tmplog = ret;
2861 last_isspace = 0;
2862 break;
2863
William Lallemandbddd4fd2012-02-27 11:23:10 +01002864 }
2865 }
2866
2867out:
William Lallemand1d705562012-03-12 12:46:41 +01002868 /* *tmplog is a unused character */
2869 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002870 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002871
Willy Tarreaubaaee002006-06-26 02:48:02 +02002872}
2873
William Lallemand1d705562012-03-12 12:46:41 +01002874/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002875 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002876 * Will not log if the frontend has no log defined.
2877 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002878void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002879{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002880 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002881 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002882 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002883
2884 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002885 err = (s->flags & SF_REDISP) ||
2886 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2887 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002888 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002889 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002890
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002891 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002892 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002893
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002894 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002895 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002896
Willy Tarreauabcd5142013-06-11 17:18:02 +02002897 if (s->logs.level) { /* loglevel was overridden */
2898 if (s->logs.level == -1) {
2899 s->logs.logwait = 0; /* logs disabled */
2900 return;
2901 }
2902 level = s->logs.level - 1;
2903 }
2904 else {
2905 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002906 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002907 level = LOG_ERR;
2908 }
William Lallemand1d705562012-03-12 12:46:41 +01002909
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002910 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002911 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002912 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002913 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002914 }
2915
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002916 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2917 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2918 &sess->fe->logformat_sd);
2919 }
2920
Dragan Dosen59cee972015-09-19 22:09:02 +02002921 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002922 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002923 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002924 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002925 s->logs.logwait = 0;
2926 }
2927}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002928
Willy Tarreau53839352018-09-05 19:51:10 +02002929/*
2930 * send a minimalist log for the session. Will not log if the frontend has no
2931 * log defined. It is assumed that this is only used to report anomalies that
2932 * cannot lead to the creation of a regular stream. Because of this the log
2933 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2934 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002935 * function to report unimportant events. It is safe to call this function with
2936 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002937 */
2938void sess_log(struct session *sess)
2939{
2940 int size, level;
2941 int sd_size = 0;
2942
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002943 if (!sess)
2944 return;
2945
Willy Tarreau53839352018-09-05 19:51:10 +02002946 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2947 return;
2948
2949 level = LOG_INFO;
2950 if (sess->fe->options2 & PR_O2_LOGERRORS)
2951 level = LOG_ERR;
2952
2953 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2954 sd_size = sess_build_logline(sess, NULL,
2955 logline_rfc5424, global.max_syslog_len,
2956 &sess->fe->logformat_sd);
2957 }
2958
2959 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2960 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002961 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Willy Tarreau53839352018-09-05 19:51:10 +02002962 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
2963 }
2964}
2965
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002966static int cli_io_handler_show_startup_logs(struct appctx *appctx)
2967{
2968 struct stream_interface *si = appctx->owner;
2969 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
2970
2971 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01002972 si_rx_room_blk(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002973 return 0;
2974 }
2975 return 1;
2976}
2977
2978/* register cli keywords */
2979static struct cli_kw_list cli_kws = {{ },{
2980 { { "show", "startup-logs", NULL },
2981 "show startup-logs : report logs emitted during HAProxy startup",
2982 NULL, cli_io_handler_show_startup_logs },
2983 {{},}
2984}};
2985
Willy Tarreau0108d902018-11-25 19:14:37 +01002986INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
2987
Willy Tarreau172f5ce2018-11-26 11:21:50 +01002988REGISTER_PER_THREAD_INIT(init_log_buffers_per_thread);
2989REGISTER_PER_THREAD_DEINIT(deinit_log_buffers_per_thread);
2990
Willy Tarreaubaaee002006-06-26 02:48:02 +02002991/*
2992 * Local variables:
2993 * c-indent-level: 8
2994 * c-basic-offset: 8
2995 * End:
2996 */