blob: e9d711c72b2026d0b5efe36abbc8e4fa2839b1f1 [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/*
695 * Parse "log" keyword and update <logsrvs> list accordingly.
696 *
697 * When <do_del> is set, it means the "no log" line was parsed, so all log
698 * servers in <logsrvs> are released.
699 *
700 * Otherwise, we try to parse the "log" line. First of all, when the list is not
701 * the global one, we look for the parameter "global". If we find it,
702 * global.logsrvs is copied. Else we parse each arguments.
703 *
704 * The function returns 1 in success case, otherwise, it returns 0 and err is
705 * filled.
706 */
707int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
708{
709 struct sockaddr_storage *sk;
710 struct logsrv *logsrv = NULL;
711 int port1, port2;
712 int cur_arg;
713
714 /*
715 * "no log": delete previous herited or defined syslog
716 * servers.
717 */
718 if (do_del) {
719 struct logsrv *back;
720
721 if (*(args[1]) != 0) {
722 memprintf(err, "'no log' does not expect arguments");
723 goto error;
724 }
725
726 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
727 LIST_DEL(&logsrv->list);
728 free(logsrv);
729 }
730 return 1;
731 }
732
733 /*
734 * "log global": copy global.logrsvs linked list to the end of logsrvs
735 * list. But first, we check (logsrvs != global.logsrvs).
736 */
737 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
738 if (logsrvs == &global.logsrvs) {
739 memprintf(err, "'global' is not supported for a global syslog server");
740 goto error;
741 }
742 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200743 struct logsrv *node;
744
745 list_for_each_entry(node, logsrvs, list) {
746 if (node->ref == logsrv)
747 goto skip_logsrv;
748 }
749
750 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200751 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200752 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200753 LIST_INIT(&node->list);
754 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200755
756 skip_logsrv:
757 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200758 }
759 return 1;
760 }
761
762 /*
763 * "log <address> ...: parse a syslog server line
764 */
765 if (*(args[1]) == 0 || *(args[2]) == 0) {
766 memprintf(err, "expects <address> and <facility> %s as arguments",
767 ((logsrvs == &global.logsrvs) ? "" : "or global"));
768 goto error;
769 }
770
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100771 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
772 if (strcmp(args[1], "stdout") == 0)
773 args[1] = "fd@1";
774 else if (strcmp(args[1], "stderr") == 0)
775 args[1] = "fd@2";
776
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200777 logsrv = calloc(1, sizeof(*logsrv));
778 if (!logsrv) {
779 memprintf(err, "out of memory");
780 goto error;
781 }
782
783 /* skip address for now, it will be parsed at the end */
784 cur_arg = 2;
785
786 /* just after the address, a length may be specified */
787 logsrv->maxlen = MAX_SYSLOG_LEN;
788 if (strcmp(args[cur_arg], "len") == 0) {
789 int len = atoi(args[cur_arg+1]);
790 if (len < 80 || len > 65535) {
791 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
792 args[cur_arg+1]);
793 goto error;
794 }
795 logsrv->maxlen = len;
796 cur_arg += 2;
797 }
798 if (logsrv->maxlen > global.max_syslog_len)
799 global.max_syslog_len = logsrv->maxlen;
800
801 /* after the length, a format may be specified */
802 if (strcmp(args[cur_arg], "format") == 0) {
803 logsrv->format = get_log_format(args[cur_arg+1]);
804 if (logsrv->format < 0) {
805 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
806 goto error;
807 }
808 cur_arg += 2;
809 }
810
811 /* parse the facility */
812 logsrv->facility = get_log_facility(args[cur_arg]);
813 if (logsrv->facility < 0) {
814 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
815 goto error;
816 }
817 cur_arg++;
818
819 /* parse the max syslog level (default: debug) */
820 logsrv->level = 7;
821 if (*(args[cur_arg])) {
822 logsrv->level = get_log_level(args[cur_arg]);
823 if (logsrv->level < 0) {
824 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
825 goto error;
826 }
827 cur_arg++;
828 }
829
830 /* parse the limit syslog level (default: emerg) */
831 logsrv->minlvl = 0;
832 if (*(args[cur_arg])) {
833 logsrv->minlvl = get_log_level(args[cur_arg]);
834 if (logsrv->minlvl < 0) {
835 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
836 goto error;
837 }
838 cur_arg++;
839 }
840
841 /* Too many args */
842 if (*(args[cur_arg])) {
843 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
844 goto error;
845 }
846
847 /* now, back to the address */
848 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
849 if (!sk)
850 goto error;
851 logsrv->addr = *sk;
852
853 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
854 if (port1 != port2) {
855 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
856 goto error;
857 }
858 logsrv->addr = *sk;
859 if (!port1)
860 set_host_port(&logsrv->addr, SYSLOG_PORT);
861 }
862 LIST_ADDQ(logsrvs, &logsrv->list);
863 return 1;
864
865 error:
866 free(logsrv);
867 return 0;
868}
869
870
Christopher Fauletd4696382017-10-24 11:44:05 +0200871/* Generic function to display messages prefixed by a label */
872static void print_message(const char *label, const char *fmt, va_list argp)
873{
874 struct tm tm;
875 char *head, *msg;
876
877 head = msg = NULL;
878
879 get_localtime(date.tv_sec, &tm);
880 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
881 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
882 memvprintf(&msg, fmt, argp);
883
884 if (global.mode & MODE_STARTING)
885 memprintf(&startup_logs, "%s%s%s", (startup_logs ? startup_logs : ""), head, msg);
886
887 fprintf(stderr, "%s%s", head, msg);
888 fflush(stderr);
889
890 free(head);
891 free(msg);
892}
893
Willy Tarreaubaaee002006-06-26 02:48:02 +0200894/*
895 * Displays the message on stderr with the date and pid. Overrides the quiet
896 * mode during startup.
897 */
Christopher Faulet767a84b2017-11-24 16:50:31 +0100898void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200899{
900 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200901
902 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
903 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +0200904 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200905 va_end(argp);
906 }
907}
908
909
910/*
911 * Displays the message on stderr with the date and pid.
912 */
Christopher Faulet767a84b2017-11-24 16:50:31 +0100913void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200914{
915 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200916
917 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
918 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +0200919 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200920 va_end(argp);
921 }
922}
923
924/*
William Lallemand9c56a222018-11-21 18:04:52 +0100925 * Displays the message on stderr with the date and pid.
926 */
927void ha_notice(const char *fmt, ...)
928{
929 va_list argp;
930
931 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
932 va_start(argp, fmt);
933 print_message("NOTICE", fmt, argp);
934 va_end(argp);
935 }
936}
937
938/*
Willy Tarreaubaaee002006-06-26 02:48:02 +0200939 * Displays the message on <out> only if quiet mode is not set.
940 */
Willy Tarreaub17916e2006-10-15 15:17:57 +0200941void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200942{
943 va_list argp;
944
945 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
946 va_start(argp, fmt);
947 vfprintf(out, fmt, argp);
948 fflush(out);
949 va_end(argp);
950 }
951}
952
953/*
Dragan Dosen1322d092015-09-22 16:05:32 +0200954 * returns log format for <fmt> or -1 if not found.
955 */
956int get_log_format(const char *fmt)
957{
958 int format;
959
960 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +0200961 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +0200962 format--;
963
964 return format;
965}
966
967/*
Willy Tarreaubaaee002006-06-26 02:48:02 +0200968 * returns log level for <lev> or -1 if not found.
969 */
970int get_log_level(const char *lev)
971{
972 int level;
973
974 level = NB_LOG_LEVELS - 1;
975 while (level >= 0 && strcmp(log_levels[level], lev))
976 level--;
977
978 return level;
979}
980
Willy Tarreaubaaee002006-06-26 02:48:02 +0200981/*
982 * returns log facility for <fac> or -1 if not found.
983 */
984int get_log_facility(const char *fac)
985{
986 int facility;
987
988 facility = NB_LOG_FACILITIES - 1;
989 while (facility >= 0 && strcmp(log_facilities[facility], fac))
990 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +0100991
Willy Tarreaubaaee002006-06-26 02:48:02 +0200992 return facility;
993}
994
William Lallemanda1cc3812012-02-08 16:38:44 +0100995/*
Dragan Dosen835b9212016-02-12 13:23:03 +0100996 * Encode the string.
997 *
998 * When using the +E log format option, it will try to escape '"\]'
999 * characters with '\' as prefix. The same prefix should not be used as
1000 * <escape>.
1001 */
1002static char *lf_encode_string(char *start, char *stop,
1003 const char escape, const fd_set *map,
1004 const char *string,
1005 struct logformat_node *node)
1006{
1007 if (node->options & LOG_OPT_ESC) {
1008 if (start < stop) {
1009 stop--; /* reserve one byte for the final '\0' */
1010 while (start < stop && *string != '\0') {
1011 if (!FD_ISSET((unsigned char)(*string), map)) {
1012 if (!FD_ISSET((unsigned char)(*string), rfc5424_escape_map))
1013 *start++ = *string;
1014 else {
1015 if (start + 2 >= stop)
1016 break;
1017 *start++ = '\\';
1018 *start++ = *string;
1019 }
1020 }
1021 else {
1022 if (start + 3 >= stop)
1023 break;
1024 *start++ = escape;
1025 *start++ = hextab[(*string >> 4) & 15];
1026 *start++ = hextab[*string & 15];
1027 }
1028 string++;
1029 }
1030 *start = '\0';
1031 }
1032 }
1033 else {
1034 return encode_string(start, stop, escape, map, string);
1035 }
1036
1037 return start;
1038}
1039
1040/*
1041 * Encode the chunk.
1042 *
1043 * When using the +E log format option, it will try to escape '"\]'
1044 * characters with '\' as prefix. The same prefix should not be used as
1045 * <escape>.
1046 */
1047static char *lf_encode_chunk(char *start, char *stop,
1048 const char escape, const fd_set *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001049 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001050 struct logformat_node *node)
1051{
1052 char *str, *end;
1053
1054 if (node->options & LOG_OPT_ESC) {
1055 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001056 str = chunk->area;
1057 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001058
1059 stop--; /* reserve one byte for the final '\0' */
1060 while (start < stop && str < end) {
1061 if (!FD_ISSET((unsigned char)(*str), map)) {
1062 if (!FD_ISSET((unsigned char)(*str), rfc5424_escape_map))
1063 *start++ = *str;
1064 else {
1065 if (start + 2 >= stop)
1066 break;
1067 *start++ = '\\';
1068 *start++ = *str;
1069 }
1070 }
1071 else {
1072 if (start + 3 >= stop)
1073 break;
1074 *start++ = escape;
1075 *start++ = hextab[(*str >> 4) & 15];
1076 *start++ = hextab[*str & 15];
1077 }
1078 str++;
1079 }
1080 *start = '\0';
1081 }
1082 }
1083 else {
1084 return encode_chunk(start, stop, escape, map, chunk);
1085 }
1086
1087 return start;
1088}
1089
1090/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001091 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001092 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001093 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001094 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001095 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001096char *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 +01001097{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001098 if (size < 2)
1099 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001100
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001101 if (node->options & LOG_OPT_QUOTE) {
1102 *(dst++) = '"';
1103 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001104 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001105
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001106 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001107 if (++len > size)
1108 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001109 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001110 char *ret;
1111
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001112 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001113 if (ret == NULL || *ret != '\0')
1114 return NULL;
1115 len = ret - dst;
1116 }
1117 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001118 len = strlcpy2(dst, src, len);
1119 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001120
1121 size -= len;
1122 dst += len;
1123 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001124 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1125 if (size < 2)
1126 return NULL;
1127 *(dst++) = '-';
1128 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001129
1130 if (node->options & LOG_OPT_QUOTE) {
1131 if (size < 2)
1132 return NULL;
1133 *(dst++) = '"';
1134 }
1135
1136 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001137 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001138}
1139
Willy Tarreau26ffa852018-09-05 15:23:10 +02001140static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001141{
1142 return lf_text_len(dst, src, size, size, node);
1143}
1144
William Lallemand5f232402012-04-05 18:02:55 +02001145/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001146 * Write a IP address to the log string
William Lallemand5f232402012-04-05 18:02:55 +02001147 * +X option write in hexadecimal notation, most signifant byte on the left
1148 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001149char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001150{
1151 char *ret = dst;
1152 int iret;
1153 char pn[INET6_ADDRSTRLEN];
1154
1155 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001156 unsigned char *addr = NULL;
1157 switch (sockaddr->sa_family) {
1158 case AF_INET:
1159 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1160 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1161 break;
1162 case AF_INET6:
1163 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1164 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1165 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1166 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1167 break;
1168 default:
1169 return NULL;
1170 }
William Lallemand5f232402012-04-05 18:02:55 +02001171 if (iret < 0 || iret > size)
1172 return NULL;
1173 ret += iret;
1174 } else {
1175 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1176 ret = lf_text(dst, pn, size, node);
1177 if (ret == NULL)
1178 return NULL;
1179 }
1180 return ret;
1181}
1182
1183/*
1184 * Write a port to the log
1185 * +X option write in hexadecimal notation, most signifant byte on the left
1186 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001187char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001188{
1189 char *ret = dst;
1190 int iret;
1191
1192 if (node->options & LOG_OPT_HEXA) {
1193 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1194 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1195 if (iret < 0 || iret > size)
1196 return NULL;
1197 ret += iret;
1198 } else {
1199 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1200 if (ret == NULL)
1201 return NULL;
1202 }
1203 return ret;
1204}
1205
Dragan Dosen1322d092015-09-22 16:05:32 +02001206/* Re-generate time-based part of the syslog header in RFC3164 format at
1207 * the beginning of logheader once a second and return the pointer to the
1208 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001209 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001210static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001211{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001212 static THREAD_LOCAL long tvsec;
1213 static THREAD_LOCAL char *dataptr = NULL; /* backup of last end of header, NULL first time */
Willy Tarreau83061a82018-07-13 11:56:34 +02001214 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001215 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001216
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001217 if (unlikely(time != tvsec || dataptr == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001218 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001219 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001220 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001221
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001222 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001223 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001224
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001225 if (unlikely(global.log_send_hostname != host.area)) {
1226 host.area = global.log_send_hostname;
1227 host.data = host.area ? strlen(host.area) : 0;
1228 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001229 }
1230
Dragan Dosen59cee972015-09-19 22:09:02 +02001231 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001232 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001233 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001234 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001235 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001236 /* WARNING: depending upon implementations, snprintf may return
1237 * either -1 or the number of bytes that would be needed to store
1238 * the total message. In both cases, we must adjust it.
1239 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001240 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1241 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001242
Dragan Dosen59cee972015-09-19 22:09:02 +02001243 dataptr = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001244 }
1245
Willy Tarreau094af4e2015-01-07 15:03:42 +01001246 dataptr[0] = 0; // ensure we get rid of any previous attempt
1247
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001248 return dataptr;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001249}
1250
Dragan Dosen1322d092015-09-22 16:05:32 +02001251/* Re-generate time-based part of the syslog header in RFC5424 format at
1252 * the beginning of logheader_rfc5424 once a second and return the pointer
1253 * to the first character after it.
1254 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001255static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001256{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001257 static THREAD_LOCAL long tvsec;
1258 static THREAD_LOCAL char *dataptr = NULL; /* backup of last end of header, NULL first time */
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001259 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001260
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001261 if (unlikely(time != tvsec || dataptr == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001262 /* this string is rebuild only once a second */
1263 struct tm tm;
1264 int hdr_len;
1265
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001266 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001267 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001268 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001269
1270 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001271 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001272 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001273 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001274 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001275 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001276 /* WARNING: depending upon implementations, snprintf may return
1277 * either -1 or the number of bytes that would be needed to store
1278 * the total message. In both cases, we must adjust it.
1279 */
1280 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1281 hdr_len = global.max_syslog_len;
1282
1283 dataptr = logheader_rfc5424 + hdr_len;
1284 }
1285
1286 dataptr[0] = 0; // ensure we get rid of any previous attempt
1287
1288 return dataptr;
1289}
1290
William Lallemand2a4a44f2012-02-06 16:00:33 +01001291/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001292 * This function sends the syslog message using a printf format string. It
1293 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001294 */
1295void send_log(struct proxy *p, int level, const char *format, ...)
1296{
1297 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001298 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001299
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001300 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001301 return;
1302
William Lallemand2a4a44f2012-02-06 16:00:33 +01001303 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001304 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001305 if (data_len < 0 || data_len > global.max_syslog_len)
1306 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001307 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001308
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001309 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001310}
1311
1312/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001313 * This function sends a syslog message to <logsrv>.
1314 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1315 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1316 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001317 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001318 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001319 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001320static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1321 int level, char *message, size_t size, char *sd, size_t sd_size,
1322 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001323{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001324 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1325 static THREAD_LOCAL struct msghdr msghdr = {
1326 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001327 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1328 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001329 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1330 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1331 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001332 time_t time = date.tv_sec;
Dragan Dosen1322d092015-09-22 16:05:32 +02001333 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001334 size_t hdr_size;
Willy Tarreau83061a82018-07-13 11:56:34 +02001335 struct buffer *tag = &global.log_tag;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001336 int fac_level;
1337 int *plogfd;
1338 char *pid_sep1 = "", *pid_sep2 = "";
1339 char logheader_short[3];
1340 int sent;
1341 int maxlen;
1342 int hdr_max = 0;
1343 int tag_max = 0;
1344 int pid_sep1_max = 0;
1345 int pid_sep2_max = 0;
1346 int sd_max = 0;
1347 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001348
1349 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001350
1351 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001352
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001353 if (logsrv->addr.ss_family == AF_UNSPEC) {
1354 /* the socket's address is a file descriptor */
1355 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1356 if (unlikely(!((struct sockaddr_in *)&logsrv->addr)->sin_port)) {
1357 /* FD not yet initialized to non-blocking mode.
1358 * DON'T DO IT ON A TERMINAL!
1359 */
1360 if (!isatty(*plogfd))
1361 fcntl(*plogfd, F_SETFL, O_NONBLOCK);
1362 ((struct sockaddr_in *)&logsrv->addr)->sin_port = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001363 }
Robert Tsai81ae1952007-12-05 10:47:29 +01001364 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001365 else if (logsrv->addr.ss_family == AF_UNIX)
1366 plogfd = &logfdunix;
1367 else
1368 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001369
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001370 if (unlikely(*plogfd < 0)) {
1371 /* socket not successfully initialized yet */
1372 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1373 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1374 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001375
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001376 if (!once) {
1377 once = 1; /* note: no need for atomic ops here */
1378 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1379 nblogger, strerror(errno), errno);
1380 }
1381 return;
1382 } else {
1383 /* we don't want to receive anything on this socket */
1384 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1385 /* does nothing under Linux, maybe needed for others */
1386 shutdown(*plogfd, SHUT_RD);
1387 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1388 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001389 }
1390
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001391 switch (logsrv->format) {
1392 case LOG_FORMAT_RFC3164:
1393 hdr = logheader;
1394 hdr_ptr = update_log_hdr(time);
1395 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001396
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001397 case LOG_FORMAT_RFC5424:
1398 hdr = logheader_rfc5424;
1399 hdr_ptr = update_log_hdr_rfc5424(time);
1400 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1401 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001402
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001403 case LOG_FORMAT_SHORT:
1404 /* all fields are known, skip the header generation */
1405 hdr = logheader_short;
1406 hdr[0] = '<';
1407 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1408 hdr[2] = '>';
1409 hdr_ptr = hdr;
1410 hdr_max = 3;
1411 maxlen = logsrv->maxlen - hdr_max;
1412 max = MIN(size, maxlen) - 1;
1413 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001414
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001415 case LOG_FORMAT_RAW:
1416 /* all fields are known, skip the header generation */
1417 hdr_ptr = hdr = "";
1418 hdr_max = 0;
1419 maxlen = logsrv->maxlen;
1420 max = MIN(size, maxlen) - 1;
1421 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001422
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001423 default:
1424 return; /* must never happen */
1425 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001426
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001427 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001428
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001429 /* For each target, we may have a different facility.
1430 * We can also have a different log level for each message.
1431 * This induces variations in the message header length.
1432 * Since we don't want to recompute it each time, nor copy it every
1433 * time, we only change the facility in the pre-computed header,
1434 * and we change the pointer to the header accordingly.
1435 */
1436 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1437 hdr_ptr = hdr + 3; /* last digit of the log level */
1438 do {
1439 *hdr_ptr = '0' + fac_level % 10;
1440 fac_level /= 10;
1441 hdr_ptr--;
1442 } while (fac_level && hdr_ptr > hdr);
1443 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001444
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001445 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001446
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001447 /* time-based header */
1448 if (unlikely(hdr_size >= logsrv->maxlen)) {
1449 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1450 sd_max = 0;
1451 goto send;
1452 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001453
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001454 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001455
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001456 /* tag */
1457 tag_max = tag->data;
1458 if (unlikely(tag_max >= maxlen)) {
1459 tag_max = maxlen - 1;
1460 sd_max = 0;
1461 goto send;
1462 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001463
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001464 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001465
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001466 /* first pid separator */
1467 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1468 if (unlikely(pid_sep1_max >= maxlen)) {
1469 pid_sep1_max = maxlen - 1;
1470 sd_max = 0;
1471 goto send;
1472 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001473
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001474 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1475 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001476
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001477 /* pid */
1478 if (unlikely(pid_size >= maxlen)) {
1479 pid_size = maxlen - 1;
1480 sd_max = 0;
1481 goto send;
1482 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001483
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001484 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001485
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001486 /* second pid separator */
1487 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1488 if (unlikely(pid_sep2_max >= maxlen)) {
1489 pid_sep2_max = maxlen - 1;
1490 sd_max = 0;
1491 goto send;
1492 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001493
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001494 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1495 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001496
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001497 /* structured-data */
1498 if (sd_max >= maxlen) {
1499 sd_max = maxlen - 1;
1500 goto send;
1501 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001502
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001503 max = MIN(size, maxlen - sd_max) - 1;
1504send:
1505 iovec[0].iov_base = hdr_ptr;
1506 iovec[0].iov_len = hdr_max;
1507 iovec[1].iov_base = tag_str;
1508 iovec[1].iov_len = tag_size;
1509 iovec[2].iov_base = pid_sep1;
1510 iovec[2].iov_len = pid_sep1_max;
1511 iovec[3].iov_base = pid_str;
1512 iovec[3].iov_len = pid_size;
1513 iovec[4].iov_base = pid_sep2;
1514 iovec[4].iov_len = pid_sep2_max;
1515 iovec[5].iov_base = sd;
1516 iovec[5].iov_len = sd_max;
1517 iovec[6].iov_base = dataptr;
1518 iovec[6].iov_len = max;
1519 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1520 iovec[7].iov_len = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001521
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001522 if (logsrv->addr.ss_family == AF_UNSPEC) {
1523 /* the target is a direct file descriptor */
1524 sent = writev(*plogfd, iovec, 8);
1525 }
1526 else {
1527 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1528 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001529
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001530 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1531 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001532
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001533 if (sent < 0) {
1534 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001535
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001536 if (errno == EAGAIN)
1537 _HA_ATOMIC_ADD(&dropped_logs, 1);
1538 else if (!once) {
1539 once = 1; /* note: no need for atomic ops here */
1540 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1541 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001542 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001543 }
1544}
Dragan Dosen59cee972015-09-19 22:09:02 +02001545
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001546/*
1547 * This function sends a syslog message.
1548 * It doesn't care about errors nor does it report them.
1549 * The arguments <sd> and <sd_size> are used for the structured-data part
1550 * in RFC5424 formatted syslog messages.
1551 */
1552void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
1553{
1554 struct list *logsrvs = NULL;
1555 struct logsrv *logsrv;
1556 int nblogger;
1557 static THREAD_LOCAL int curr_pid;
1558 static THREAD_LOCAL char pidstr[100];
1559 static THREAD_LOCAL struct buffer pid;
1560 struct buffer *tag = &global.log_tag;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001561
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001562 if (p == NULL) {
1563 if (!LIST_ISEMPTY(&global.logsrvs)) {
1564 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001565 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001566 } else {
1567 if (!LIST_ISEMPTY(&p->logsrvs)) {
1568 logsrvs = &p->logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001569 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001570 if (p->log_tag.area) {
1571 tag = &p->log_tag;
1572 }
1573 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001574
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001575 if (!logsrvs)
1576 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001577
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001578 if (unlikely(curr_pid != getpid())) {
1579 curr_pid = getpid();
1580 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1581 chunk_initstr(&pid, pidstr);
1582 }
1583
1584 /* Send log messages to syslog server. */
1585 nblogger = 0;
1586 list_for_each_entry(logsrv, logsrvs, list) {
1587 /* we can filter the level of the messages that are sent to each logger */
1588 if (level > logsrv->level)
1589 continue;
1590
1591 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1592 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001593 }
1594}
1595
William Lallemandbddd4fd2012-02-27 11:23:10 +01001596extern fd_set hdr_encode_map[];
1597extern fd_set url_encode_map[];
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001598extern fd_set http_encode_map[];
William Lallemandbddd4fd2012-02-27 11:23:10 +01001599
Willy Tarreaubaaee002006-06-26 02:48:02 +02001600
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001601const 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 +01001602const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1603 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1604 Set-cookie Updated, unknown, unknown */
1605
William Lallemand1d705562012-03-12 12:46:41 +01001606/*
1607 * try to write a character if there is enough space, or goto out
1608 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001609#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001610 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001611 *(tmplog++) = (x); \
1612 } else { \
1613 goto out; \
1614 } \
1615 } while(0)
1616
Dragan Dosen835b9212016-02-12 13:23:03 +01001617
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001618/* Initializes some log data at boot */
1619static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001620{
1621 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001622 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001623
1624 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1625 * inside PARAM-VALUE should be escaped with '\' as prefix.
1626 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1627 * details.
1628 */
1629 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1630
1631 tmp = "\"\\]";
1632 while (*tmp) {
1633 FD_SET(*tmp, rfc5424_escape_map);
1634 tmp++;
1635 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001636
1637 /* initialize the log header encoding map : '{|}"#' should be encoded with
1638 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1639 * URL encoding only requires '"', '#' to be encoded as well as non-
1640 * printable characters above.
1641 */
1642 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1643 memset(url_encode_map, 0, sizeof(url_encode_map));
1644 for (i = 0; i < 32; i++) {
1645 FD_SET(i, hdr_encode_map);
1646 FD_SET(i, url_encode_map);
1647 }
1648 for (i = 127; i < 256; i++) {
1649 FD_SET(i, hdr_encode_map);
1650 FD_SET(i, url_encode_map);
1651 }
1652
1653 tmp = "\"#{|}";
1654 while (*tmp) {
1655 FD_SET(*tmp, hdr_encode_map);
1656 tmp++;
1657 }
1658
1659 tmp = "\"#";
1660 while (*tmp) {
1661 FD_SET(*tmp, url_encode_map);
1662 tmp++;
1663 }
1664
1665 /* initialize the http header encoding map. The draft httpbis define the
1666 * header content as:
1667 *
1668 * HTTP-message = start-line
1669 * *( header-field CRLF )
1670 * CRLF
1671 * [ message-body ]
1672 * header-field = field-name ":" OWS field-value OWS
1673 * field-value = *( field-content / obs-fold )
1674 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1675 * obs-fold = CRLF 1*( SP / HTAB )
1676 * field-vchar = VCHAR / obs-text
1677 * VCHAR = %x21-7E
1678 * obs-text = %x80-FF
1679 *
1680 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1681 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001682 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001683 */
1684 memset(http_encode_map, 0, sizeof(http_encode_map));
1685 for (i = 0x00; i <= 0x08; i++)
1686 FD_SET(i, http_encode_map);
1687 for (i = 0x0a; i <= 0x1f; i++)
1688 FD_SET(i, http_encode_map);
1689 FD_SET(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001690}
William Lallemand1d705562012-03-12 12:46:41 +01001691
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001692INITCALL0(STG_PREPARE, init_log);
1693
Christopher Fauletcd7879a2017-10-27 13:53:47 +02001694static int init_log_buffers_per_thread()
1695{
1696 return init_log_buffers();
1697}
1698
1699static void deinit_log_buffers_per_thread()
1700{
1701 deinit_log_buffers();
1702}
1703
Christopher Faulet0132d062017-07-26 15:33:35 +02001704/* Initialize log buffers used for syslog messages */
1705int init_log_buffers()
1706{
1707 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
1708 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
1709 logline = my_realloc2(logline, global.max_syslog_len + 1);
1710 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1711 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1712 return 0;
1713 return 1;
1714}
1715
1716/* Deinitialize log buffers used for syslog messages */
1717void deinit_log_buffers()
1718{
Olivier Houchard7c497112019-03-07 14:19:24 +01001719 void *tmp_startup_logs;
1720
Christopher Faulet0132d062017-07-26 15:33:35 +02001721 free(logheader);
1722 free(logheader_rfc5424);
1723 free(logline);
1724 free(logline_rfc5424);
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001725 tmp_startup_logs = _HA_ATOMIC_XCHG(&startup_logs, NULL);
Olivier Houchard7c497112019-03-07 14:19:24 +01001726 free(tmp_startup_logs);
1727
Christopher Faulet0132d062017-07-26 15:33:35 +02001728 logheader = NULL;
1729 logheader_rfc5424 = NULL;
1730 logline = NULL;
1731 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001732 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001733}
1734
Willy Tarreaudf974472012-12-28 02:44:01 +01001735/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1736 * <maxsize> characters. Returns the size of the output string in characters,
1737 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001738 * is not zero. It requires a valid session and optionally a stream. If the
1739 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001740 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001741int 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 +02001742{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001743 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001744 struct proxy *be;
1745 struct http_txn *txn;
1746 const struct strm_logs *logs;
1747 const struct connection *be_conn;
1748 unsigned int s_flags;
1749 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001750 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001751 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001752 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001753 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001754 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001755 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001756 int t_request;
1757 int hdr;
1758 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001759 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001760 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001761 char *ret;
1762 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001763 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001764 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001765 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001766
William Lallemandbddd4fd2012-02-27 11:23:10 +01001767 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001768
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001769 if (likely(s)) {
1770 be = s->be;
1771 txn = s->txn;
1772 be_conn = cs_conn(objt_cs(s->si[1].end));
1773 s_flags = s->flags;
1774 uniq_id = s->uniq_id;
1775 logs = &s->logs;
1776 } else {
1777 /* we have no stream so we first need to initialize a few
1778 * things that are needed later. We do increment the request
1779 * ID so that it's uniquely assigned to this request just as
1780 * if the request had reached the point of being processed.
1781 * A request error is reported as it's the only element we have
1782 * here and which justifies emitting such a log.
1783 */
1784 be = fe;
1785 txn = NULL;
1786 be_conn = NULL;
1787 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001788 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001789
1790 /* prepare a valid log structure */
1791 tmp_strm_log.tv_accept = sess->tv_accept;
1792 tmp_strm_log.accept_date = sess->accept_date;
1793 tmp_strm_log.t_handshake = sess->t_handshake;
1794 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1795 tv_zero(&tmp_strm_log.tv_request);
1796 tmp_strm_log.t_queue = -1;
1797 tmp_strm_log.t_connect = -1;
1798 tmp_strm_log.t_data = -1;
1799 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1800 tmp_strm_log.bytes_in = 0;
1801 tmp_strm_log.bytes_out = 0;
1802 tmp_strm_log.prx_queue_pos = 0;
1803 tmp_strm_log.srv_queue_pos = 0;
1804
1805 logs = &tmp_strm_log;
1806 }
1807
William Lallemandbddd4fd2012-02-27 11:23:10 +01001808 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001809 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1810 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001811
William Lallemand1d705562012-03-12 12:46:41 +01001812 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001813
William Lallemandbddd4fd2012-02-27 11:23:10 +01001814 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001815 if (LIST_ISEMPTY(list_format))
1816 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001817
William Lallemand1d705562012-03-12 12:46:41 +01001818 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001819 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001820 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001821 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001822 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001823
Willy Tarreauc8368452012-12-21 00:09:23 +01001824 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01001825 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01001826 if (!last_isspace) {
1827 LOGCHAR(' ');
1828 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001829 }
1830 break;
1831
William Lallemand1d705562012-03-12 12:46:41 +01001832 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01001833 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02001834 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001835 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001836 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001837 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001838 last_isspace = 0;
1839 break;
1840
Willy Tarreauc8368452012-12-21 00:09:23 +01001841 case LOG_FMT_EXPR: // sample expression, may be request or response
1842 key = NULL;
Olivier Houchardf90db442018-12-15 14:00:06 +01001843 if (tmp->options & LOG_OPT_REQ_CAP && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02001844 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 +01001845 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02001846 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 +01001847 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01001848 ret = lf_encode_chunk(tmplog, dst + maxsize,
1849 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001850 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001851 ret = lf_text_len(tmplog,
1852 key ? key->data.u.str.area : NULL,
1853 key ? key->data.u.str.data : 0,
1854 dst + maxsize - tmplog,
1855 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01001856 if (ret == 0)
1857 goto out;
1858 tmplog = ret;
1859 last_isspace = 0;
1860 break;
1861
Willy Tarreau2beef582012-12-20 17:22:52 +01001862 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001863 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001864 if (conn)
1865 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.from, dst + maxsize - tmplog, tmp);
1866 else
1867 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01001868 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001869 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001870 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001871 last_isspace = 0;
1872 break;
1873
Willy Tarreau2beef582012-12-20 17:22:52 +01001874 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001875 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001876 if (conn) {
1877 if (conn->addr.from.ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02001878 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001879 } else {
1880 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.from,
1881 dst + maxsize - tmplog, tmp);
1882 }
William Lallemand5f232402012-04-05 18:02:55 +02001883 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001884 else
1885 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1886
William Lallemand5f232402012-04-05 18:02:55 +02001887 if (ret == NULL)
1888 goto out;
1889 tmplog = ret;
1890 last_isspace = 0;
1891 break;
1892
Willy Tarreau2beef582012-12-20 17:22:52 +01001893 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001894 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001895 if (conn) {
1896 conn_get_to_addr(conn);
1897 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
1898 }
1899 else
1900 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1901
William Lallemand1d705562012-03-12 12:46:41 +01001902 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001903 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001904 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001905 last_isspace = 0;
1906 break;
1907
Willy Tarreau2beef582012-12-20 17:22:52 +01001908 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001909 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001910 if (conn) {
1911 conn_get_to_addr(conn);
1912 if (conn->addr.to.ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02001913 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001914 else
1915 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02001916 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001917 else
1918 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1919
William Lallemand5f232402012-04-05 18:02:55 +02001920 if (ret == NULL)
1921 goto out;
1922 tmplog = ret;
1923 last_isspace = 0;
1924 break;
1925
Willy Tarreau2beef582012-12-20 17:22:52 +01001926 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001927 if (be_conn)
1928 ret = lf_ip(tmplog, (const struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001929 else
1930 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1931
William Lallemand1d705562012-03-12 12:46:41 +01001932 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01001933 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001934 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01001935 last_isspace = 0;
1936 break;
1937
Willy Tarreau2beef582012-12-20 17:22:52 +01001938 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001939 if (be_conn)
1940 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001941 else
1942 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1943
William Lallemand5f232402012-04-05 18:02:55 +02001944 if (ret == NULL)
1945 goto out;
1946 tmplog = ret;
1947 last_isspace = 0;
1948 break;
1949
Willy Tarreau2beef582012-12-20 17:22:52 +01001950 case LOG_FMT_SERVERIP: // %si
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001951 if (be_conn)
1952 ret = lf_ip(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001953 else
1954 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1955
William Lallemand5f232402012-04-05 18:02:55 +02001956 if (ret == NULL)
1957 goto out;
1958 tmplog = ret;
1959 last_isspace = 0;
1960 break;
1961
Willy Tarreau2beef582012-12-20 17:22:52 +01001962 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001963 if (be_conn)
1964 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001965 else
1966 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1967
William Lallemand1d705562012-03-12 12:46:41 +01001968 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01001969 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001970 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01001971 last_isspace = 0;
1972 break;
1973
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001974 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001975 get_localtime(logs->accept_date.tv_sec, &tm);
1976 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001977 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001978 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001979 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001980 last_isspace = 0;
1981 break;
1982
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001983 case LOG_FMT_tr: // %tr = start of request date
1984 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001985 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 +02001986 get_localtime(tv.tv_sec, &tm);
1987 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
1988 if (ret == NULL)
1989 goto out;
1990 tmplog = ret;
1991 last_isspace = 0;
1992 break;
1993
1994 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001995 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02001996 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001997 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001998 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001999 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002000 last_isspace = 0;
2001 break;
2002
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002003 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002004 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 +02002005 get_gmtime(tv.tv_sec, &tm);
2006 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2007 if (ret == NULL)
2008 goto out;
2009 tmplog = ret;
2010 last_isspace = 0;
2011 break;
2012
2013 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002014 get_localtime(logs->accept_date.tv_sec, &tm);
2015 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002016 if (ret == NULL)
2017 goto out;
2018 tmplog = ret;
2019 last_isspace = 0;
2020 break;
2021
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002022 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002023 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 +02002024 get_localtime(tv.tv_sec, &tm);
2025 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2026 if (ret == NULL)
2027 goto out;
2028 tmplog = ret;
2029 last_isspace = 0;
2030 break;
2031
William Lallemand5f232402012-04-05 18:02:55 +02002032 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002033 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002034 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002035 if (iret < 0 || iret > dst + maxsize - tmplog)
2036 goto out;
2037 last_isspace = 0;
2038 tmplog += iret;
2039 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002040 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002041 if (ret == NULL)
2042 goto out;
2043 tmplog = ret;
2044 last_isspace = 0;
2045 }
2046 break;
2047
William Lallemand1d705562012-03-12 12:46:41 +01002048 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002049 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002050 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002051 if (iret < 0 || iret > dst + maxsize - tmplog)
2052 goto out;
2053 last_isspace = 0;
2054 tmplog += iret;
2055 } else {
2056 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002057 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002058 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002059 tmplog, 4);
2060 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002061 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002062 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002063 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002064 }
2065 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002066
William Lallemand1d705562012-03-12 12:46:41 +01002067 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002068 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002069 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002070 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002071 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002072 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002073 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002074 break;
2075
Willy Tarreau773d65f2012-10-12 14:56:11 +02002076 case LOG_FMT_FRONTEND_XPRT: // %ft
2077 src = fe->id;
2078 if (tmp->options & LOG_OPT_QUOTE)
2079 LOGCHAR('"');
2080 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2081 if (iret == 0)
2082 goto out;
2083 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002084 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002085 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002086 if (tmp->options & LOG_OPT_QUOTE)
2087 LOGCHAR('"');
2088 last_isspace = 0;
2089 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002090#ifdef USE_OPENSSL
2091 case LOG_FMT_SSL_CIPHER: // %sslc
2092 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002093 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002094 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002095 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002096 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002097 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2098 if (ret == NULL)
2099 goto out;
2100 tmplog = ret;
2101 last_isspace = 0;
2102 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002103
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002104 case LOG_FMT_SSL_VERSION: // %sslv
2105 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002106 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002107 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002108 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002109 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002110 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2111 if (ret == NULL)
2112 goto out;
2113 tmplog = ret;
2114 last_isspace = 0;
2115 break;
2116#endif
William Lallemand1d705562012-03-12 12:46:41 +01002117 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002118 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002119 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002120 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002121 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002122 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002123 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002124 break;
2125
William Lallemand1d705562012-03-12 12:46:41 +01002126 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002127 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002128 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002129 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002130 break;
2131 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002132 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002133 break;
2134 default:
2135 src = "<NOSRV>";
2136 break;
2137 }
William Lallemand5f232402012-04-05 18:02:55 +02002138 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002139 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002140 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002141 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002142 last_isspace = 0;
2143 break;
2144
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002145 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002146 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002147 if (ret == NULL)
2148 goto out;
2149 tmplog = ret;
2150 last_isspace = 0;
2151 break;
2152
2153 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002154 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002155 if (ret == NULL)
2156 goto out;
2157 tmplog = ret;
2158 last_isspace = 0;
2159 break;
2160
2161 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002162 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002163 tmplog, dst + maxsize - tmplog);
2164 if (ret == NULL)
2165 goto out;
2166 tmplog = ret;
2167 last_isspace = 0;
2168 break;
2169
2170 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002171 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002172 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002173 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002174 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002175 last_isspace = 0;
2176 break;
2177
William Lallemand1d705562012-03-12 12:46:41 +01002178 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002179 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002180 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002181 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002182 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002183 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002184 last_isspace = 0;
2185 break;
2186
William Lallemand1d705562012-03-12 12:46:41 +01002187 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002188 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002189 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002190 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002191 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002192 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002193 last_isspace = 0;
2194 break;
2195
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002196 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002197 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002198 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002199 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002200 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002201 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002202 last_isspace = 0;
2203 break;
2204
Willy Tarreau27b639d2016-05-17 17:55:27 +02002205 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002206 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002207 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002208 tmplog, dst + maxsize - tmplog);
2209 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002210 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002211 tmplog, dst + maxsize - tmplog);
2212 if (ret == NULL)
2213 goto out;
2214 tmplog = ret;
2215 last_isspace = 0;
2216 break;
2217
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002218 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2219 if (!(fe->to_log & LW_BYTES))
2220 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002221 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 +02002222 tmplog, dst + maxsize - tmplog);
2223 if (ret == NULL)
2224 goto out;
2225 tmplog = ret;
2226 last_isspace = 0;
2227 break;
2228
2229 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002230 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002231 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002232 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002233 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002234 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002235 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002236 last_isspace = 0;
2237 break;
2238
Willy Tarreau2beef582012-12-20 17:22:52 +01002239 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002240 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002241 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002242 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002243 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002244 last_isspace = 0;
2245 break;
2246
William Lallemand1d705562012-03-12 12:46:41 +01002247 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002248 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002249 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002250 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002251 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002252 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002253 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002254 last_isspace = 0;
2255 break;
2256
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002257 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002258 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002259 if (ret == NULL)
2260 goto out;
2261 tmplog = ret;
2262 last_isspace = 0;
2263 break;
2264
Willy Tarreau2beef582012-12-20 17:22:52 +01002265 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002266 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002267 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002268 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002269 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002270 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002271 last_isspace = 0;
2272 break;
2273
Willy Tarreau2beef582012-12-20 17:22:52 +01002274 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002275 src = txn ? txn->srv_cookie : NULL;
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 Lallemand51b5dca2012-03-26 17:52:55 +02002278 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
William Lallemand1d705562012-03-12 12:46:41 +01002283 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002284 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2285 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002286 *tmplog = '\0';
2287 last_isspace = 0;
2288 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002289
William Lallemand1d705562012-03-12 12:46:41 +01002290 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002291 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2292 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002293 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2294 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 +01002295 last_isspace = 0;
2296 break;
2297
William Lallemand1d705562012-03-12 12:46:41 +01002298 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002299 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002300 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002301 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002302 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002303 last_isspace = 0;
2304 break;
2305
William Lallemand1d705562012-03-12 12:46:41 +01002306 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002307 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002308 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002309 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002310 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002311 last_isspace = 0;
2312 break;
2313
William Lallemand1d705562012-03-12 12:46:41 +01002314 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002315 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002316 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002317 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002318 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002319 last_isspace = 0;
2320 break;
2321
William Lallemand1d705562012-03-12 12:46:41 +01002322 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002323 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002324 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002325 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002326 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002327 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002328 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002329 last_isspace = 0;
2330 break;
2331
William Lallemand1d705562012-03-12 12:46:41 +01002332 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002333 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002334 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002335 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002336 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002337 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002338 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002339 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002340 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002341 last_isspace = 0;
2342 break;
2343
William Lallemand1d705562012-03-12 12:46:41 +01002344 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002345 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002346 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002347 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002348 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002349 last_isspace = 0;
2350 break;
2351
William Lallemand1d705562012-03-12 12:46:41 +01002352 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002353 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002354 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002355 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002356 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002357 last_isspace = 0;
2358 break;
2359
William Lallemand1d705562012-03-12 12:46:41 +01002360 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002361 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002362 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002363 if (tmp->options & LOG_OPT_QUOTE)
2364 LOGCHAR('"');
2365 LOGCHAR('{');
2366 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2367 if (hdr)
2368 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002369 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002370 ret = lf_encode_string(tmplog, dst + maxsize,
2371 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002372 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002373 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002374 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002375 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002376 }
2377 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002378 if (tmp->options & LOG_OPT_QUOTE)
2379 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002380 last_isspace = 0;
2381 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002382 break;
2383
William Lallemand1d705562012-03-12 12:46:41 +01002384 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002385 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002386 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002387 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2388 if (hdr > 0)
2389 LOGCHAR(' ');
2390 if (tmp->options & LOG_OPT_QUOTE)
2391 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002392 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002393 ret = lf_encode_string(tmplog, dst + maxsize,
2394 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002395 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002396 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002397 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002398 } else if (!(tmp->options & LOG_OPT_QUOTE))
2399 LOGCHAR('-');
2400 if (tmp->options & LOG_OPT_QUOTE)
2401 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002402 last_isspace = 0;
2403 }
2404 }
2405 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002406
William Lallemand1d705562012-03-12 12:46:41 +01002407
2408 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002409 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002410 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002411 if (tmp->options & LOG_OPT_QUOTE)
2412 LOGCHAR('"');
2413 LOGCHAR('{');
2414 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2415 if (hdr)
2416 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002417 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002418 ret = lf_encode_string(tmplog, dst + maxsize,
2419 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002420 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002421 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002422 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002423 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002424 }
2425 LOGCHAR('}');
2426 last_isspace = 0;
2427 if (tmp->options & LOG_OPT_QUOTE)
2428 LOGCHAR('"');
2429 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002430 break;
2431
William Lallemand1d705562012-03-12 12:46:41 +01002432 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002433 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002434 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002435 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2436 if (hdr > 0)
2437 LOGCHAR(' ');
2438 if (tmp->options & LOG_OPT_QUOTE)
2439 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002440 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002441 ret = lf_encode_string(tmplog, dst + maxsize,
2442 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002443 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002444 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002445 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002446 } else if (!(tmp->options & LOG_OPT_QUOTE))
2447 LOGCHAR('-');
2448 if (tmp->options & LOG_OPT_QUOTE)
2449 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002450 last_isspace = 0;
2451 }
2452 }
2453 break;
2454
William Lallemand1d705562012-03-12 12:46:41 +01002455 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002456 /* Request */
2457 if (tmp->options & LOG_OPT_QUOTE)
2458 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002459 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002460 ret = lf_encode_string(tmplog, dst + maxsize,
2461 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002462 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002463 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002464 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002465 if (tmp->options & LOG_OPT_QUOTE)
2466 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002467 last_isspace = 0;
2468 break;
William Lallemand5f232402012-04-05 18:02:55 +02002469
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002470 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002471 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002472
Willy Tarreaub7636d12015-06-17 19:58:02 +02002473 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002474 LOGCHAR('"');
2475
2476 end = uri + strlen(uri);
2477 // look for the first whitespace character
2478 while (uri < end && !HTTP_IS_SPHT(*uri))
2479 uri++;
2480
2481 // keep advancing past multiple spaces
2482 while (uri < end && HTTP_IS_SPHT(*uri)) {
2483 uri++; nspaces++;
2484 }
2485
2486 // look for first space or question mark after url
2487 spc = uri;
2488 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2489 spc++;
2490
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002491 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002492 chunk.area = "<BADREQ>";
2493 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002494 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002495 chunk.area = uri;
2496 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002497 }
2498
Dragan Dosen835b9212016-02-12 13:23:03 +01002499 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002500 if (ret == NULL || *ret != '\0')
2501 goto out;
2502
2503 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002504 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002505 LOGCHAR('"');
2506
2507 last_isspace = 0;
2508 break;
2509
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002510 case LOG_FMT_HTTP_QUERY: // %HQ
2511 if (tmp->options & LOG_OPT_QUOTE)
2512 LOGCHAR('"');
2513
Willy Tarreau57bc8912016-04-25 17:09:40 +02002514 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002515 chunk.area = "<BADREQ>";
2516 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002517 } else {
2518 uri = txn->uri;
2519 end = uri + strlen(uri);
2520 // look for the first question mark
2521 while (uri < end && *uri != '?')
2522 uri++;
2523
2524 qmark = uri;
2525 // look for first space or question mark after url
2526 while (uri < end && !HTTP_IS_SPHT(*uri))
2527 uri++;
2528
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002529 chunk.area = qmark;
2530 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002531 }
2532
Dragan Dosen835b9212016-02-12 13:23:03 +01002533 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002534 if (ret == NULL || *ret != '\0')
2535 goto out;
2536
2537 tmplog = ret;
2538 if (tmp->options & LOG_OPT_QUOTE)
2539 LOGCHAR('"');
2540
2541 last_isspace = 0;
2542 break;
2543
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002544 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002545 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002546
Willy Tarreaub7636d12015-06-17 19:58:02 +02002547 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002548 LOGCHAR('"');
2549
2550 end = uri + strlen(uri);
2551 // look for the first whitespace character
2552 while (uri < end && !HTTP_IS_SPHT(*uri))
2553 uri++;
2554
2555 // keep advancing past multiple spaces
2556 while (uri < end && HTTP_IS_SPHT(*uri)) {
2557 uri++; nspaces++;
2558 }
2559
2560 // look for first space after url
2561 spc = uri;
2562 while (spc < end && !HTTP_IS_SPHT(*spc))
2563 spc++;
2564
Willy Tarreau57bc8912016-04-25 17:09:40 +02002565 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002566 chunk.area = "<BADREQ>";
2567 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002568 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002569 chunk.area = uri;
2570 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002571 }
2572
Dragan Dosen835b9212016-02-12 13:23:03 +01002573 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002574 if (ret == NULL || *ret != '\0')
2575 goto out;
2576
2577 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002578 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002579 LOGCHAR('"');
2580
2581 last_isspace = 0;
2582 break;
2583
2584 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002585 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002586 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002587 LOGCHAR('"');
2588
2589 end = uri + strlen(uri);
2590 // look for the first whitespace character
2591 spc = uri;
2592 while (spc < end && !HTTP_IS_SPHT(*spc))
2593 spc++;
2594
2595 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002596 chunk.area = "<BADREQ>";
2597 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002598 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002599 chunk.area = uri;
2600 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002601 }
2602
Dragan Dosen835b9212016-02-12 13:23:03 +01002603 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002604 if (ret == NULL || *ret != '\0')
2605 goto out;
2606
2607 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002608 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002609 LOGCHAR('"');
2610
2611 last_isspace = 0;
2612 break;
2613
2614 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002615 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002616 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002617 LOGCHAR('"');
2618
2619 end = uri + strlen(uri);
2620 // look for the first whitespace character
2621 while (uri < end && !HTTP_IS_SPHT(*uri))
2622 uri++;
2623
2624 // keep advancing past multiple spaces
2625 while (uri < end && HTTP_IS_SPHT(*uri)) {
2626 uri++; nspaces++;
2627 }
2628
2629 // look for the next whitespace character
2630 while (uri < end && !HTTP_IS_SPHT(*uri))
2631 uri++;
2632
2633 // keep advancing past multiple spaces
2634 while (uri < end && HTTP_IS_SPHT(*uri))
2635 uri++;
2636
Willy Tarreau57bc8912016-04-25 17:09:40 +02002637 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002638 chunk.area = "<BADREQ>";
2639 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002640 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002641 chunk.area = "HTTP/0.9";
2642 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002643 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002644 chunk.area = uri;
2645 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002646 }
2647
Dragan Dosen835b9212016-02-12 13:23:03 +01002648 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002649 if (ret == NULL || *ret != '\0')
2650 goto out;
2651
2652 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002653 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002654 LOGCHAR('"');
2655
2656 last_isspace = 0;
2657 break;
2658
William Lallemand5f232402012-04-05 18:02:55 +02002659 case LOG_FMT_COUNTER: // %rt
2660 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002661 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002662 if (iret < 0 || iret > dst + maxsize - tmplog)
2663 goto out;
2664 last_isspace = 0;
2665 tmplog += iret;
2666 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002667 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002668 if (ret == NULL)
2669 goto out;
2670 tmplog = ret;
2671 last_isspace = 0;
2672 }
2673 break;
2674
Willy Tarreau7346acb2014-08-28 15:03:15 +02002675 case LOG_FMT_LOGCNT: // %lc
2676 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002677 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002678 if (iret < 0 || iret > dst + maxsize - tmplog)
2679 goto out;
2680 last_isspace = 0;
2681 tmplog += iret;
2682 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002683 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002684 if (ret == NULL)
2685 goto out;
2686 tmplog = ret;
2687 last_isspace = 0;
2688 }
2689 break;
2690
William Lallemand5f232402012-04-05 18:02:55 +02002691 case LOG_FMT_HOSTNAME: // %H
2692 src = hostname;
2693 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2694 if (ret == NULL)
2695 goto out;
2696 tmplog = ret;
2697 last_isspace = 0;
2698 break;
2699
2700 case LOG_FMT_PID: // %pid
2701 if (tmp->options & LOG_OPT_HEXA) {
2702 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2703 if (iret < 0 || iret > dst + maxsize - tmplog)
2704 goto out;
2705 last_isspace = 0;
2706 tmplog += iret;
2707 } else {
2708 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2709 if (ret == NULL)
2710 goto out;
2711 tmplog = ret;
2712 last_isspace = 0;
2713 }
2714 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002715
2716 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002717 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002718 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002719 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002720 if (ret == NULL)
2721 goto out;
2722 tmplog = ret;
2723 last_isspace = 0;
2724 break;
2725
William Lallemandbddd4fd2012-02-27 11:23:10 +01002726 }
2727 }
2728
2729out:
William Lallemand1d705562012-03-12 12:46:41 +01002730 /* *tmplog is a unused character */
2731 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002732 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002733
Willy Tarreaubaaee002006-06-26 02:48:02 +02002734}
2735
William Lallemand1d705562012-03-12 12:46:41 +01002736/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002737 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002738 * Will not log if the frontend has no log defined.
2739 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002740void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002741{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002742 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002743 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002744 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002745
2746 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002747 err = (s->flags & SF_REDISP) ||
2748 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2749 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002750 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002751 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002752
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002753 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002754 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002755
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002756 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002757 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002758
Willy Tarreauabcd5142013-06-11 17:18:02 +02002759 if (s->logs.level) { /* loglevel was overridden */
2760 if (s->logs.level == -1) {
2761 s->logs.logwait = 0; /* logs disabled */
2762 return;
2763 }
2764 level = s->logs.level - 1;
2765 }
2766 else {
2767 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002768 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002769 level = LOG_ERR;
2770 }
William Lallemand1d705562012-03-12 12:46:41 +01002771
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002772 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002773 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002774 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002775 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002776 }
2777
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002778 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2779 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2780 &sess->fe->logformat_sd);
2781 }
2782
Dragan Dosen59cee972015-09-19 22:09:02 +02002783 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002784 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002785 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002786 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002787 s->logs.logwait = 0;
2788 }
2789}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002790
Willy Tarreau53839352018-09-05 19:51:10 +02002791/*
2792 * send a minimalist log for the session. Will not log if the frontend has no
2793 * log defined. It is assumed that this is only used to report anomalies that
2794 * cannot lead to the creation of a regular stream. Because of this the log
2795 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2796 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002797 * function to report unimportant events. It is safe to call this function with
2798 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002799 */
2800void sess_log(struct session *sess)
2801{
2802 int size, level;
2803 int sd_size = 0;
2804
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002805 if (!sess)
2806 return;
2807
Willy Tarreau53839352018-09-05 19:51:10 +02002808 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2809 return;
2810
2811 level = LOG_INFO;
2812 if (sess->fe->options2 & PR_O2_LOGERRORS)
2813 level = LOG_ERR;
2814
2815 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2816 sd_size = sess_build_logline(sess, NULL,
2817 logline_rfc5424, global.max_syslog_len,
2818 &sess->fe->logformat_sd);
2819 }
2820
2821 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2822 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002823 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Willy Tarreau53839352018-09-05 19:51:10 +02002824 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
2825 }
2826}
2827
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002828static int cli_io_handler_show_startup_logs(struct appctx *appctx)
2829{
2830 struct stream_interface *si = appctx->owner;
2831 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
2832
2833 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01002834 si_rx_room_blk(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002835 return 0;
2836 }
2837 return 1;
2838}
2839
2840/* register cli keywords */
2841static struct cli_kw_list cli_kws = {{ },{
2842 { { "show", "startup-logs", NULL },
2843 "show startup-logs : report logs emitted during HAProxy startup",
2844 NULL, cli_io_handler_show_startup_logs },
2845 {{},}
2846}};
2847
Willy Tarreau0108d902018-11-25 19:14:37 +01002848INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
2849
Willy Tarreau172f5ce2018-11-26 11:21:50 +01002850REGISTER_PER_THREAD_INIT(init_log_buffers_per_thread);
2851REGISTER_PER_THREAD_DEINIT(deinit_log_buffers_per_thread);
2852
Willy Tarreaubaaee002006-06-26 02:48:02 +02002853/*
2854 * Local variables:
2855 * c-indent-level: 8
2856 * c-basic-offset: 8
2857 * End:
2858 */