blob: 896219468688f991757e7ebf4524e70dfca91255 [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 Tarreau2dd0d472006-06-29 17:53:05 +020029#include <common/standard.h>
Willy Tarreaufb278672006-10-15 15:38:50 +020030#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031
Christopher Fauletc1b730a2017-10-24 12:00:51 +020032#include <types/cli.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020033#include <types/global.h>
William Lallemand723b73a2012-02-08 16:37:49 +010034#include <types/log.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020035
Christopher Fauletc1b730a2017-10-24 12:00:51 +020036#include <proto/applet.h>
37#include <proto/cli.h>
William Lallemand5f232402012-04-05 18:02:55 +020038#include <proto/frontend.h>
Willy Tarreau35b51c62018-09-10 15:38:55 +020039#include <proto/h1.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. */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200253static THREAD_LOCAL 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
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100579 /* The prinicple of the two-step state machine below is to first detect a change, and
580 * 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/*
925 * Displays the message on <out> only if quiet mode is not set.
926 */
Willy Tarreaub17916e2006-10-15 15:17:57 +0200927void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200928{
929 va_list argp;
930
931 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
932 va_start(argp, fmt);
933 vfprintf(out, fmt, argp);
934 fflush(out);
935 va_end(argp);
936 }
937}
938
939/*
Dragan Dosen1322d092015-09-22 16:05:32 +0200940 * returns log format for <fmt> or -1 if not found.
941 */
942int get_log_format(const char *fmt)
943{
944 int format;
945
946 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +0200947 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +0200948 format--;
949
950 return format;
951}
952
953/*
Willy Tarreaubaaee002006-06-26 02:48:02 +0200954 * returns log level for <lev> or -1 if not found.
955 */
956int get_log_level(const char *lev)
957{
958 int level;
959
960 level = NB_LOG_LEVELS - 1;
961 while (level >= 0 && strcmp(log_levels[level], lev))
962 level--;
963
964 return level;
965}
966
Willy Tarreaubaaee002006-06-26 02:48:02 +0200967/*
968 * returns log facility for <fac> or -1 if not found.
969 */
970int get_log_facility(const char *fac)
971{
972 int facility;
973
974 facility = NB_LOG_FACILITIES - 1;
975 while (facility >= 0 && strcmp(log_facilities[facility], fac))
976 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +0100977
Willy Tarreaubaaee002006-06-26 02:48:02 +0200978 return facility;
979}
980
William Lallemanda1cc3812012-02-08 16:38:44 +0100981/*
Dragan Dosen835b9212016-02-12 13:23:03 +0100982 * Encode the string.
983 *
984 * When using the +E log format option, it will try to escape '"\]'
985 * characters with '\' as prefix. The same prefix should not be used as
986 * <escape>.
987 */
988static char *lf_encode_string(char *start, char *stop,
989 const char escape, const fd_set *map,
990 const char *string,
991 struct logformat_node *node)
992{
993 if (node->options & LOG_OPT_ESC) {
994 if (start < stop) {
995 stop--; /* reserve one byte for the final '\0' */
996 while (start < stop && *string != '\0') {
997 if (!FD_ISSET((unsigned char)(*string), map)) {
998 if (!FD_ISSET((unsigned char)(*string), rfc5424_escape_map))
999 *start++ = *string;
1000 else {
1001 if (start + 2 >= stop)
1002 break;
1003 *start++ = '\\';
1004 *start++ = *string;
1005 }
1006 }
1007 else {
1008 if (start + 3 >= stop)
1009 break;
1010 *start++ = escape;
1011 *start++ = hextab[(*string >> 4) & 15];
1012 *start++ = hextab[*string & 15];
1013 }
1014 string++;
1015 }
1016 *start = '\0';
1017 }
1018 }
1019 else {
1020 return encode_string(start, stop, escape, map, string);
1021 }
1022
1023 return start;
1024}
1025
1026/*
1027 * Encode the chunk.
1028 *
1029 * When using the +E log format option, it will try to escape '"\]'
1030 * characters with '\' as prefix. The same prefix should not be used as
1031 * <escape>.
1032 */
1033static char *lf_encode_chunk(char *start, char *stop,
1034 const char escape, const fd_set *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001035 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001036 struct logformat_node *node)
1037{
1038 char *str, *end;
1039
1040 if (node->options & LOG_OPT_ESC) {
1041 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001042 str = chunk->area;
1043 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001044
1045 stop--; /* reserve one byte for the final '\0' */
1046 while (start < stop && str < end) {
1047 if (!FD_ISSET((unsigned char)(*str), map)) {
1048 if (!FD_ISSET((unsigned char)(*str), rfc5424_escape_map))
1049 *start++ = *str;
1050 else {
1051 if (start + 2 >= stop)
1052 break;
1053 *start++ = '\\';
1054 *start++ = *str;
1055 }
1056 }
1057 else {
1058 if (start + 3 >= stop)
1059 break;
1060 *start++ = escape;
1061 *start++ = hextab[(*str >> 4) & 15];
1062 *start++ = hextab[*str & 15];
1063 }
1064 str++;
1065 }
1066 *start = '\0';
1067 }
1068 }
1069 else {
1070 return encode_chunk(start, stop, escape, map, chunk);
1071 }
1072
1073 return start;
1074}
1075
1076/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001077 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001078 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001079 *
1080 * Return the adress of the \0 character, or NULL on error
1081 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001082char *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 +01001083{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001084 if (size < 2)
1085 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001086
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001087 if (node->options & LOG_OPT_QUOTE) {
1088 *(dst++) = '"';
1089 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001090 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001091
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001092 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001093 if (++len > size)
1094 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001095 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001096 char *ret;
1097
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001098 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001099 if (ret == NULL || *ret != '\0')
1100 return NULL;
1101 len = ret - dst;
1102 }
1103 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001104 len = strlcpy2(dst, src, len);
1105 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001106
1107 size -= len;
1108 dst += len;
1109 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001110 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1111 if (size < 2)
1112 return NULL;
1113 *(dst++) = '-';
1114 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001115
1116 if (node->options & LOG_OPT_QUOTE) {
1117 if (size < 2)
1118 return NULL;
1119 *(dst++) = '"';
1120 }
1121
1122 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001123 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001124}
1125
Willy Tarreau26ffa852018-09-05 15:23:10 +02001126static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001127{
1128 return lf_text_len(dst, src, size, size, node);
1129}
1130
William Lallemand5f232402012-04-05 18:02:55 +02001131/*
1132 * Write a IP adress to the log string
1133 * +X option write in hexadecimal notation, most signifant byte on the left
1134 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001135char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001136{
1137 char *ret = dst;
1138 int iret;
1139 char pn[INET6_ADDRSTRLEN];
1140
1141 if (node->options & LOG_OPT_HEXA) {
1142 const unsigned char *addr = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1143 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1144 if (iret < 0 || iret > size)
1145 return NULL;
1146 ret += iret;
1147 } else {
1148 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1149 ret = lf_text(dst, pn, size, node);
1150 if (ret == NULL)
1151 return NULL;
1152 }
1153 return ret;
1154}
1155
1156/*
1157 * Write a port to the log
1158 * +X option write in hexadecimal notation, most signifant byte on the left
1159 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001160char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001161{
1162 char *ret = dst;
1163 int iret;
1164
1165 if (node->options & LOG_OPT_HEXA) {
1166 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1167 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1168 if (iret < 0 || iret > size)
1169 return NULL;
1170 ret += iret;
1171 } else {
1172 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1173 if (ret == NULL)
1174 return NULL;
1175 }
1176 return ret;
1177}
1178
Dragan Dosen1322d092015-09-22 16:05:32 +02001179/* Re-generate time-based part of the syslog header in RFC3164 format at
1180 * the beginning of logheader once a second and return the pointer to the
1181 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001182 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001183static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001184{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001185 static THREAD_LOCAL long tvsec;
1186 static THREAD_LOCAL char *dataptr = NULL; /* backup of last end of header, NULL first time */
Willy Tarreau83061a82018-07-13 11:56:34 +02001187 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001188 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001189
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001190 if (unlikely(time != tvsec || dataptr == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001191 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001192 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001193 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001194
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001195 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001196 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001197
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001198 if (unlikely(global.log_send_hostname != host.area)) {
1199 host.area = global.log_send_hostname;
1200 host.data = host.area ? strlen(host.area) : 0;
1201 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001202 }
1203
Dragan Dosen59cee972015-09-19 22:09:02 +02001204 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001205 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001206 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001207 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001208 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001209 /* WARNING: depending upon implementations, snprintf may return
1210 * either -1 or the number of bytes that would be needed to store
1211 * the total message. In both cases, we must adjust it.
1212 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001213 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1214 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001215
Dragan Dosen59cee972015-09-19 22:09:02 +02001216 dataptr = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001217 }
1218
Willy Tarreau094af4e2015-01-07 15:03:42 +01001219 dataptr[0] = 0; // ensure we get rid of any previous attempt
1220
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001221 return dataptr;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001222}
1223
Dragan Dosen1322d092015-09-22 16:05:32 +02001224/* Re-generate time-based part of the syslog header in RFC5424 format at
1225 * the beginning of logheader_rfc5424 once a second and return the pointer
1226 * to the first character after it.
1227 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001228static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001229{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001230 static THREAD_LOCAL long tvsec;
1231 static THREAD_LOCAL char *dataptr = NULL; /* backup of last end of header, NULL first time */
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001232 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001233
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001234 if (unlikely(time != tvsec || dataptr == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001235 /* this string is rebuild only once a second */
1236 struct tm tm;
1237 int hdr_len;
1238
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001239 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001240 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001241 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001242
1243 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001244 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001245 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001246 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001247 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001248 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001249 /* WARNING: depending upon implementations, snprintf may return
1250 * either -1 or the number of bytes that would be needed to store
1251 * the total message. In both cases, we must adjust it.
1252 */
1253 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1254 hdr_len = global.max_syslog_len;
1255
1256 dataptr = logheader_rfc5424 + hdr_len;
1257 }
1258
1259 dataptr[0] = 0; // ensure we get rid of any previous attempt
1260
1261 return dataptr;
1262}
1263
William Lallemand2a4a44f2012-02-06 16:00:33 +01001264/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001265 * This function sends the syslog message using a printf format string. It
1266 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001267 */
1268void send_log(struct proxy *p, int level, const char *format, ...)
1269{
1270 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001271 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001272
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001273 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001274 return;
1275
William Lallemand2a4a44f2012-02-06 16:00:33 +01001276 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001277 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001278 if (data_len < 0 || data_len > global.max_syslog_len)
1279 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001280 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001281
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001282 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001283}
1284
1285/*
1286 * This function sends a syslog message.
1287 * It doesn't care about errors nor does it report them.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001288 * It overrides the last byte of the message vector with an LF character.
1289 * The arguments <sd> and <sd_size> are used for the structured-data part
1290 * in RFC5424 formatted syslog messages.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001291 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001292void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001293{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001294 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1295 static THREAD_LOCAL struct msghdr msghdr = {
1296 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001297 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1298 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001299 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1300 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1301 static THREAD_LOCAL char *dataptr = NULL;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001302 int fac_level;
1303 struct list *logsrvs = NULL;
1304 struct logsrv *tmp = NULL;
1305 int nblogger;
Dragan Dosen1322d092015-09-22 16:05:32 +02001306 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001307 size_t hdr_size;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001308 time_t time = date.tv_sec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001309 struct buffer *tag = &global.log_tag;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001310 static THREAD_LOCAL int curr_pid;
1311 static THREAD_LOCAL char pidstr[100];
Willy Tarreau83061a82018-07-13 11:56:34 +02001312 static THREAD_LOCAL struct buffer pid;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001313
1314 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001315
1316 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001317
1318 if (p == NULL) {
William Lallemand0f99e342011-10-12 17:50:54 +02001319 if (!LIST_ISEMPTY(&global.logsrvs)) {
1320 logsrvs = &global.logsrvs;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001321 }
1322 } else {
William Lallemand0f99e342011-10-12 17:50:54 +02001323 if (!LIST_ISEMPTY(&p->logsrvs)) {
1324 logsrvs = &p->logsrvs;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001325 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001326 if (p->log_tag.area) {
Dragan Dosen43885c72015-10-01 13:18:13 +02001327 tag = &p->log_tag;
1328 }
Robert Tsai81ae1952007-12-05 10:47:29 +01001329 }
1330
William Lallemand0f99e342011-10-12 17:50:54 +02001331 if (!logsrvs)
1332 return;
1333
Dragan Dosen43885c72015-10-01 13:18:13 +02001334 if (unlikely(curr_pid != getpid())) {
1335 curr_pid = getpid();
1336 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1337 chunk_initstr(&pid, pidstr);
1338 }
1339
Robert Tsai81ae1952007-12-05 10:47:29 +01001340 /* Send log messages to syslog server. */
William Lallemand0f99e342011-10-12 17:50:54 +02001341 nblogger = 0;
1342 list_for_each_entry(tmp, logsrvs, list) {
1343 const struct logsrv *logsrv = tmp;
David du Colombier11bcb6c2011-03-24 12:23:00 +01001344 int *plogfd = logsrv->addr.ss_family == AF_UNIX ?
Robert Tsai81ae1952007-12-05 10:47:29 +01001345 &logfdunix : &logfdinet;
Willy Tarreaue8746a02018-11-12 08:45:00 +01001346 char *pid_sep1 = "", *pid_sep2 = "";
1347 char logheader_short[3];
Robert Tsai81ae1952007-12-05 10:47:29 +01001348 int sent;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001349 int maxlen;
Dragan Dosen59cee972015-09-19 22:09:02 +02001350 int hdr_max = 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001351 int tag_max = 0;
1352 int pid_sep1_max = 0;
1353 int pid_max = 0;
1354 int pid_sep2_max = 0;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001355 int sd_max = 0;
Dragan Dosen5b78d9b2015-09-28 16:01:03 +02001356 int max = 0;
Robert Tsai81ae1952007-12-05 10:47:29 +01001357
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001358 nblogger++;
1359
Willy Tarreaubaaee002006-06-26 02:48:02 +02001360 /* we can filter the level of the messages that are sent to each logger */
William Lallemand0f99e342011-10-12 17:50:54 +02001361 if (level > logsrv->level)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001362 continue;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001363
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001364 if (unlikely(*plogfd < 0)) {
1365 /* socket not successfully initialized yet */
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001366 if (logsrv->addr.ss_family == AF_UNSPEC) {
1367 /* the socket's address is a file descriptor */
1368 *plogfd = ((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1369 fcntl(*plogfd, F_SETFL, O_NONBLOCK);
1370 }
1371 else if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1372 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001373 static char once;
1374
1375 if (!once) {
1376 once = 1; /* note: no need for atomic ops here */
Willy Tarreau251fe342018-11-12 07:00:11 +01001377 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001378 nblogger, strerror(errno), errno);
1379 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001380 continue;
William Lallemanda8b26712018-11-13 18:30:12 +01001381 } else {
1382 /* we don't want to receive anything on this socket */
1383 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1384 /* does nothing under Linux, maybe needed for others */
1385 shutdown(*plogfd, SHUT_RD);
1386 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001387 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001388 }
1389
Dragan Dosen1322d092015-09-22 16:05:32 +02001390 switch (logsrv->format) {
1391 case LOG_FORMAT_RFC3164:
1392 hdr = logheader;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001393 hdr_ptr = update_log_hdr(time);
Dragan Dosen1322d092015-09-22 16:05:32 +02001394 break;
1395
1396 case LOG_FORMAT_RFC5424:
1397 hdr = logheader_rfc5424;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001398 hdr_ptr = update_log_hdr_rfc5424(time);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001399 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
Dragan Dosen1322d092015-09-22 16:05:32 +02001400 break;
1401
Willy Tarreaue8746a02018-11-12 08:45:00 +01001402 case LOG_FORMAT_SHORT:
1403 /* all fields are known, skip the header generation */
1404 hdr = logheader_short;
1405 hdr[0] = '<';
1406 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1407 hdr[2] = '>';
1408 hdr_ptr = hdr;
1409 hdr_max = 3;
1410 maxlen = logsrv->maxlen - hdr_max;
1411 max = MIN(size, maxlen) - 1;
1412 goto send;
1413
Willy Tarreauc1b06452018-11-12 11:57:56 +01001414 case LOG_FORMAT_RAW:
1415 /* all fields are known, skip the header generation */
1416 hdr_ptr = hdr = "";
1417 hdr_max = 0;
1418 maxlen = logsrv->maxlen;
1419 max = MIN(size, maxlen) - 1;
1420 goto send;
1421
Dragan Dosen1322d092015-09-22 16:05:32 +02001422 default:
1423 continue; /* must never happen */
1424 }
1425
1426 hdr_size = hdr_ptr - hdr;
1427
Willy Tarreaubaaee002006-06-26 02:48:02 +02001428 /* For each target, we may have a different facility.
1429 * We can also have a different log level for each message.
1430 * This induces variations in the message header length.
1431 * Since we don't want to recompute it each time, nor copy it every
1432 * time, we only change the facility in the pre-computed header,
1433 * and we change the pointer to the header accordingly.
1434 */
William Lallemand0f99e342011-10-12 17:50:54 +02001435 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
Dragan Dosen1322d092015-09-22 16:05:32 +02001436 hdr_ptr = hdr + 3; /* last digit of the log level */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001437 do {
Dragan Dosen59cee972015-09-19 22:09:02 +02001438 *hdr_ptr = '0' + fac_level % 10;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001439 fac_level /= 10;
Dragan Dosen59cee972015-09-19 22:09:02 +02001440 hdr_ptr--;
Dragan Dosen1322d092015-09-22 16:05:32 +02001441 } while (fac_level && hdr_ptr > hdr);
Dragan Dosen59cee972015-09-19 22:09:02 +02001442 *hdr_ptr = '<';
William Lallemand2a4a44f2012-02-06 16:00:33 +01001443
Dragan Dosen1322d092015-09-22 16:05:32 +02001444 hdr_max = hdr_size - (hdr_ptr - hdr);
Dragan Dosen59cee972015-09-19 22:09:02 +02001445
Dragan Dosen43885c72015-10-01 13:18:13 +02001446 /* time-based header */
Dragan Dosen59cee972015-09-19 22:09:02 +02001447 if (unlikely(hdr_size >= logsrv->maxlen)) {
1448 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001449 sd_max = 0;
Dragan Dosen59cee972015-09-19 22:09:02 +02001450 goto send;
1451 }
1452
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001453 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001454
Dragan Dosen43885c72015-10-01 13:18:13 +02001455 /* tag */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001456 tag_max = tag->data;
Dragan Dosen43885c72015-10-01 13:18:13 +02001457 if (unlikely(tag_max >= maxlen)) {
1458 tag_max = maxlen - 1;
1459 sd_max = 0;
1460 goto send;
1461 }
1462
1463 maxlen -= tag_max;
1464
1465 /* first pid separator */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001466 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
Dragan Dosen43885c72015-10-01 13:18:13 +02001467 if (unlikely(pid_sep1_max >= maxlen)) {
1468 pid_sep1_max = maxlen - 1;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001469 sd_max = 0;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001470 goto send;
1471 }
1472
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001473 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
Dragan Dosen43885c72015-10-01 13:18:13 +02001474 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001475
Dragan Dosen43885c72015-10-01 13:18:13 +02001476 /* pid */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001477 pid_max = pid.data;
Dragan Dosen43885c72015-10-01 13:18:13 +02001478 if (unlikely(pid_max >= maxlen)) {
1479 pid_max = maxlen - 1;
1480 sd_max = 0;
1481 goto send;
1482 }
1483
1484 maxlen -= pid_max;
1485
1486 /* second pid separator */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001487 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
Dragan Dosen43885c72015-10-01 13:18:13 +02001488 if (unlikely(pid_sep2_max >= maxlen)) {
1489 pid_sep2_max = maxlen - 1;
1490 sd_max = 0;
1491 goto send;
1492 }
1493
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001494 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
Dragan Dosen43885c72015-10-01 13:18:13 +02001495 maxlen -= pid_sep2_max;
1496
1497 /* structured-data */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001498 if (sd_max >= maxlen) {
1499 sd_max = maxlen - 1;
1500 goto send;
1501 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001502
Dragan Dosen5b78d9b2015-09-28 16:01:03 +02001503 max = MIN(size, maxlen - sd_max) - 1;
Dragan Dosen59cee972015-09-19 22:09:02 +02001504send:
Dragan Dosen59cee972015-09-19 22:09:02 +02001505 iovec[0].iov_base = hdr_ptr;
Dragan Dosen43885c72015-10-01 13:18:13 +02001506 iovec[0].iov_len = hdr_max;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001507 iovec[1].iov_base = tag->area;
Dragan Dosen43885c72015-10-01 13:18:13 +02001508 iovec[1].iov_len = tag_max;
1509 iovec[2].iov_base = pid_sep1;
1510 iovec[2].iov_len = pid_sep1_max;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001511 iovec[3].iov_base = pid.area;
Dragan Dosen43885c72015-10-01 13:18:13 +02001512 iovec[3].iov_len = pid_max;
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 Dosen609ac2a2015-09-16 18:25:42 +02001521
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001522 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 Dosen609ac2a2015-09-16 18:25:42 +02001529
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001530 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1531 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001532
Robert Tsai81ae1952007-12-05 10:47:29 +01001533 if (sent < 0) {
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001534 static char once;
1535
Willy Tarreau13ef7732018-11-12 07:25:28 +01001536 if (errno == EAGAIN)
1537 HA_ATOMIC_ADD(&dropped_logs, 1);
1538 else if (!once) {
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001539 once = 1; /* note: no need for atomic ops here */
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001540 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001541 nblogger, strerror(errno), errno);
1542 }
Robert Tsai81ae1952007-12-05 10:47:29 +01001543 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001544 }
1545}
1546
William Lallemandbddd4fd2012-02-27 11:23:10 +01001547extern fd_set hdr_encode_map[];
1548extern fd_set url_encode_map[];
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001549extern fd_set http_encode_map[];
William Lallemandbddd4fd2012-02-27 11:23:10 +01001550
Willy Tarreaubaaee002006-06-26 02:48:02 +02001551
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001552const 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 +01001553const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1554 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1555 Set-cookie Updated, unknown, unknown */
1556
William Lallemand1d705562012-03-12 12:46:41 +01001557/*
1558 * try to write a character if there is enough space, or goto out
1559 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001560#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001561 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001562 *(tmplog++) = (x); \
1563 } else { \
1564 goto out; \
1565 } \
1566 } while(0)
1567
Dragan Dosen835b9212016-02-12 13:23:03 +01001568
1569/* Initializes some log data.
1570 */
1571void init_log()
1572{
1573 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001574 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001575
1576 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1577 * inside PARAM-VALUE should be escaped with '\' as prefix.
1578 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1579 * details.
1580 */
1581 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1582
1583 tmp = "\"\\]";
1584 while (*tmp) {
1585 FD_SET(*tmp, rfc5424_escape_map);
1586 tmp++;
1587 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001588
1589 /* initialize the log header encoding map : '{|}"#' should be encoded with
1590 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1591 * URL encoding only requires '"', '#' to be encoded as well as non-
1592 * printable characters above.
1593 */
1594 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1595 memset(url_encode_map, 0, sizeof(url_encode_map));
1596 for (i = 0; i < 32; i++) {
1597 FD_SET(i, hdr_encode_map);
1598 FD_SET(i, url_encode_map);
1599 }
1600 for (i = 127; i < 256; i++) {
1601 FD_SET(i, hdr_encode_map);
1602 FD_SET(i, url_encode_map);
1603 }
1604
1605 tmp = "\"#{|}";
1606 while (*tmp) {
1607 FD_SET(*tmp, hdr_encode_map);
1608 tmp++;
1609 }
1610
1611 tmp = "\"#";
1612 while (*tmp) {
1613 FD_SET(*tmp, url_encode_map);
1614 tmp++;
1615 }
1616
1617 /* initialize the http header encoding map. The draft httpbis define the
1618 * header content as:
1619 *
1620 * HTTP-message = start-line
1621 * *( header-field CRLF )
1622 * CRLF
1623 * [ message-body ]
1624 * header-field = field-name ":" OWS field-value OWS
1625 * field-value = *( field-content / obs-fold )
1626 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1627 * obs-fold = CRLF 1*( SP / HTAB )
1628 * field-vchar = VCHAR / obs-text
1629 * VCHAR = %x21-7E
1630 * obs-text = %x80-FF
1631 *
1632 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1633 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
1634 * "obs-fold" is volontary forgotten because haproxy remove this.
1635 */
1636 memset(http_encode_map, 0, sizeof(http_encode_map));
1637 for (i = 0x00; i <= 0x08; i++)
1638 FD_SET(i, http_encode_map);
1639 for (i = 0x0a; i <= 0x1f; i++)
1640 FD_SET(i, http_encode_map);
1641 FD_SET(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001642}
William Lallemand1d705562012-03-12 12:46:41 +01001643
Christopher Fauletcd7879a2017-10-27 13:53:47 +02001644static int init_log_buffers_per_thread()
1645{
1646 return init_log_buffers();
1647}
1648
1649static void deinit_log_buffers_per_thread()
1650{
1651 deinit_log_buffers();
1652}
1653
Christopher Faulet0132d062017-07-26 15:33:35 +02001654/* Initialize log buffers used for syslog messages */
1655int init_log_buffers()
1656{
1657 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
1658 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
1659 logline = my_realloc2(logline, global.max_syslog_len + 1);
1660 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1661 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1662 return 0;
1663 return 1;
1664}
1665
1666/* Deinitialize log buffers used for syslog messages */
1667void deinit_log_buffers()
1668{
1669 free(logheader);
1670 free(logheader_rfc5424);
1671 free(logline);
1672 free(logline_rfc5424);
Christopher Fauletd4696382017-10-24 11:44:05 +02001673 free(startup_logs);
Christopher Faulet0132d062017-07-26 15:33:35 +02001674 logheader = NULL;
1675 logheader_rfc5424 = NULL;
1676 logline = NULL;
1677 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001678 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001679}
1680
Willy Tarreaudf974472012-12-28 02:44:01 +01001681/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1682 * <maxsize> characters. Returns the size of the output string in characters,
1683 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001684 * is not zero. It requires a valid session and optionally a stream. If the
1685 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001686 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001687int 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 +02001688{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001689 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001690 struct proxy *be;
1691 struct http_txn *txn;
1692 const struct strm_logs *logs;
1693 const struct connection *be_conn;
1694 unsigned int s_flags;
1695 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001696 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001697 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001698 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001699 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001700 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001701 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001702 int t_request;
1703 int hdr;
1704 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001705 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001706 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001707 char *ret;
1708 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001709 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001710 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001711 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001712
William Lallemandbddd4fd2012-02-27 11:23:10 +01001713 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001714
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001715 if (likely(s)) {
1716 be = s->be;
1717 txn = s->txn;
1718 be_conn = cs_conn(objt_cs(s->si[1].end));
1719 s_flags = s->flags;
1720 uniq_id = s->uniq_id;
1721 logs = &s->logs;
1722 } else {
1723 /* we have no stream so we first need to initialize a few
1724 * things that are needed later. We do increment the request
1725 * ID so that it's uniquely assigned to this request just as
1726 * if the request had reached the point of being processed.
1727 * A request error is reported as it's the only element we have
1728 * here and which justifies emitting such a log.
1729 */
1730 be = fe;
1731 txn = NULL;
1732 be_conn = NULL;
1733 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
1734 uniq_id = HA_ATOMIC_XADD(&global.req_count, 1);
1735
1736 /* prepare a valid log structure */
1737 tmp_strm_log.tv_accept = sess->tv_accept;
1738 tmp_strm_log.accept_date = sess->accept_date;
1739 tmp_strm_log.t_handshake = sess->t_handshake;
1740 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1741 tv_zero(&tmp_strm_log.tv_request);
1742 tmp_strm_log.t_queue = -1;
1743 tmp_strm_log.t_connect = -1;
1744 tmp_strm_log.t_data = -1;
1745 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1746 tmp_strm_log.bytes_in = 0;
1747 tmp_strm_log.bytes_out = 0;
1748 tmp_strm_log.prx_queue_pos = 0;
1749 tmp_strm_log.srv_queue_pos = 0;
1750
1751 logs = &tmp_strm_log;
1752 }
1753
William Lallemandbddd4fd2012-02-27 11:23:10 +01001754 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001755 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1756 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001757
William Lallemand1d705562012-03-12 12:46:41 +01001758 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001759
William Lallemandbddd4fd2012-02-27 11:23:10 +01001760 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001761 if (LIST_ISEMPTY(list_format))
1762 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001763
William Lallemand1d705562012-03-12 12:46:41 +01001764 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001765 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001766 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001767 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001768 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001769
Willy Tarreauc8368452012-12-21 00:09:23 +01001770 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01001771 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01001772 if (!last_isspace) {
1773 LOGCHAR(' ');
1774 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001775 }
1776 break;
1777
William Lallemand1d705562012-03-12 12:46:41 +01001778 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01001779 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02001780 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001781 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001782 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001783 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001784 last_isspace = 0;
1785 break;
1786
Willy Tarreauc8368452012-12-21 00:09:23 +01001787 case LOG_FMT_EXPR: // sample expression, may be request or response
1788 key = NULL;
1789 if (tmp->options & LOG_OPT_REQ_CAP)
Adis Nezirovic79beb242015-07-06 15:41:02 +02001790 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Willy Tarreauc8368452012-12-21 00:09:23 +01001791 if (!key && (tmp->options & LOG_OPT_RES_CAP))
Adis Nezirovic79beb242015-07-06 15:41:02 +02001792 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 +01001793 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01001794 ret = lf_encode_chunk(tmplog, dst + maxsize,
1795 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001796 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001797 ret = lf_text_len(tmplog,
1798 key ? key->data.u.str.area : NULL,
1799 key ? key->data.u.str.data : 0,
1800 dst + maxsize - tmplog,
1801 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01001802 if (ret == 0)
1803 goto out;
1804 tmplog = ret;
1805 last_isspace = 0;
1806 break;
1807
Willy Tarreau2beef582012-12-20 17:22:52 +01001808 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001809 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001810 if (conn)
1811 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.from, dst + maxsize - tmplog, tmp);
1812 else
1813 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01001814 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001815 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001816 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001817 last_isspace = 0;
1818 break;
1819
Willy Tarreau2beef582012-12-20 17:22:52 +01001820 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001821 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001822 if (conn) {
1823 if (conn->addr.from.ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02001824 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001825 } else {
1826 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.from,
1827 dst + maxsize - tmplog, tmp);
1828 }
William Lallemand5f232402012-04-05 18:02:55 +02001829 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001830 else
1831 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1832
William Lallemand5f232402012-04-05 18:02:55 +02001833 if (ret == NULL)
1834 goto out;
1835 tmplog = ret;
1836 last_isspace = 0;
1837 break;
1838
Willy Tarreau2beef582012-12-20 17:22:52 +01001839 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001840 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001841 if (conn) {
1842 conn_get_to_addr(conn);
1843 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
1844 }
1845 else
1846 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1847
William Lallemand1d705562012-03-12 12:46:41 +01001848 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001849 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001850 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001851 last_isspace = 0;
1852 break;
1853
Willy Tarreau2beef582012-12-20 17:22:52 +01001854 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02001855 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001856 if (conn) {
1857 conn_get_to_addr(conn);
1858 if (conn->addr.to.ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02001859 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001860 else
1861 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02001862 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001863 else
1864 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1865
William Lallemand5f232402012-04-05 18:02:55 +02001866 if (ret == NULL)
1867 goto out;
1868 tmplog = ret;
1869 last_isspace = 0;
1870 break;
1871
Willy Tarreau2beef582012-12-20 17:22:52 +01001872 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001873 if (be_conn)
1874 ret = lf_ip(tmplog, (const struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001875 else
1876 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1877
William Lallemand1d705562012-03-12 12:46:41 +01001878 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01001879 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001880 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01001881 last_isspace = 0;
1882 break;
1883
Willy Tarreau2beef582012-12-20 17:22:52 +01001884 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001885 if (be_conn)
1886 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001887 else
1888 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1889
William Lallemand5f232402012-04-05 18:02:55 +02001890 if (ret == NULL)
1891 goto out;
1892 tmplog = ret;
1893 last_isspace = 0;
1894 break;
1895
Willy Tarreau2beef582012-12-20 17:22:52 +01001896 case LOG_FMT_SERVERIP: // %si
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001897 if (be_conn)
1898 ret = lf_ip(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001899 else
1900 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1901
William Lallemand5f232402012-04-05 18:02:55 +02001902 if (ret == NULL)
1903 goto out;
1904 tmplog = ret;
1905 last_isspace = 0;
1906 break;
1907
Willy Tarreau2beef582012-12-20 17:22:52 +01001908 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02001909 if (be_conn)
1910 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001911 else
1912 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
1913
William Lallemand1d705562012-03-12 12:46:41 +01001914 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01001915 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001916 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01001917 last_isspace = 0;
1918 break;
1919
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001920 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001921 get_localtime(logs->accept_date.tv_sec, &tm);
1922 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001923 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001924 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001925 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001926 last_isspace = 0;
1927 break;
1928
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001929 case LOG_FMT_tr: // %tr = start of request date
1930 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001931 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 +02001932 get_localtime(tv.tv_sec, &tm);
1933 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
1934 if (ret == NULL)
1935 goto out;
1936 tmplog = ret;
1937 last_isspace = 0;
1938 break;
1939
1940 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001941 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02001942 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001943 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001944 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01001945 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001946 last_isspace = 0;
1947 break;
1948
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001949 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001950 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 +02001951 get_gmtime(tv.tv_sec, &tm);
1952 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
1953 if (ret == NULL)
1954 goto out;
1955 tmplog = ret;
1956 last_isspace = 0;
1957 break;
1958
1959 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001960 get_localtime(logs->accept_date.tv_sec, &tm);
1961 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08001962 if (ret == NULL)
1963 goto out;
1964 tmplog = ret;
1965 last_isspace = 0;
1966 break;
1967
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001968 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001969 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 +02001970 get_localtime(tv.tv_sec, &tm);
1971 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
1972 if (ret == NULL)
1973 goto out;
1974 tmplog = ret;
1975 last_isspace = 0;
1976 break;
1977
William Lallemand5f232402012-04-05 18:02:55 +02001978 case LOG_FMT_TS: // %Ts
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001979 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02001980 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001981 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02001982 if (iret < 0 || iret > dst + maxsize - tmplog)
1983 goto out;
1984 last_isspace = 0;
1985 tmplog += iret;
1986 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001987 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02001988 if (ret == NULL)
1989 goto out;
1990 tmplog = ret;
1991 last_isspace = 0;
1992 }
1993 break;
1994
William Lallemand1d705562012-03-12 12:46:41 +01001995 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02001996 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001997 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02001998 if (iret < 0 || iret > dst + maxsize - tmplog)
1999 goto out;
2000 last_isspace = 0;
2001 tmplog += iret;
2002 } else {
2003 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002004 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002005 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002006 tmplog, 4);
2007 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002008 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002009 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002010 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002011 }
2012 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002013
William Lallemand1d705562012-03-12 12:46:41 +01002014 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002015 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002016 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002017 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002018 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002019 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002020 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002021 break;
2022
Willy Tarreau773d65f2012-10-12 14:56:11 +02002023 case LOG_FMT_FRONTEND_XPRT: // %ft
2024 src = fe->id;
2025 if (tmp->options & LOG_OPT_QUOTE)
2026 LOGCHAR('"');
2027 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2028 if (iret == 0)
2029 goto out;
2030 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002031 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002032 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002033 if (tmp->options & LOG_OPT_QUOTE)
2034 LOGCHAR('"');
2035 last_isspace = 0;
2036 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002037#ifdef USE_OPENSSL
2038 case LOG_FMT_SSL_CIPHER: // %sslc
2039 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002040 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002041 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002042 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002043 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002044 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2045 if (ret == NULL)
2046 goto out;
2047 tmplog = ret;
2048 last_isspace = 0;
2049 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002050
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002051 case LOG_FMT_SSL_VERSION: // %sslv
2052 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002053 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002054 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002055 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002056 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002057 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2058 if (ret == NULL)
2059 goto out;
2060 tmplog = ret;
2061 last_isspace = 0;
2062 break;
2063#endif
William Lallemand1d705562012-03-12 12:46:41 +01002064 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002065 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002066 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002067 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002068 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002069 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002070 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002071 break;
2072
William Lallemand1d705562012-03-12 12:46:41 +01002073 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002074 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002075 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002076 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002077 break;
2078 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002079 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002080 break;
2081 default:
2082 src = "<NOSRV>";
2083 break;
2084 }
William Lallemand5f232402012-04-05 18:02:55 +02002085 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002086 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002087 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002088 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002089 last_isspace = 0;
2090 break;
2091
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002092 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002093 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002094 if (ret == NULL)
2095 goto out;
2096 tmplog = ret;
2097 last_isspace = 0;
2098 break;
2099
2100 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002101 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002102 if (ret == NULL)
2103 goto out;
2104 tmplog = ret;
2105 last_isspace = 0;
2106 break;
2107
2108 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002109 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002110 tmplog, dst + maxsize - tmplog);
2111 if (ret == NULL)
2112 goto out;
2113 tmplog = ret;
2114 last_isspace = 0;
2115 break;
2116
2117 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002118 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002119 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002120 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002121 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002122 last_isspace = 0;
2123 break;
2124
William Lallemand1d705562012-03-12 12:46:41 +01002125 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002126 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002127 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002128 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002129 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002130 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002131 last_isspace = 0;
2132 break;
2133
William Lallemand1d705562012-03-12 12:46:41 +01002134 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002135 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002136 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002137 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002138 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002139 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002140 last_isspace = 0;
2141 break;
2142
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002143 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002144 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002145 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002146 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002147 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002148 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002149 last_isspace = 0;
2150 break;
2151
Willy Tarreau27b639d2016-05-17 17:55:27 +02002152 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002153 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002154 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002155 tmplog, dst + maxsize - tmplog);
2156 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002157 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002158 tmplog, dst + maxsize - tmplog);
2159 if (ret == NULL)
2160 goto out;
2161 tmplog = ret;
2162 last_isspace = 0;
2163 break;
2164
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002165 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2166 if (!(fe->to_log & LW_BYTES))
2167 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002168 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 +02002169 tmplog, dst + maxsize - tmplog);
2170 if (ret == NULL)
2171 goto out;
2172 tmplog = ret;
2173 last_isspace = 0;
2174 break;
2175
2176 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002177 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002178 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002179 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002180 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002181 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002182 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002183 last_isspace = 0;
2184 break;
2185
Willy Tarreau2beef582012-12-20 17:22:52 +01002186 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002187 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002188 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002189 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002190 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002191 last_isspace = 0;
2192 break;
2193
William Lallemand1d705562012-03-12 12:46:41 +01002194 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002195 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002196 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002197 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002198 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002199 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002200 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002201 last_isspace = 0;
2202 break;
2203
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002204 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002205 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002206 if (ret == NULL)
2207 goto out;
2208 tmplog = ret;
2209 last_isspace = 0;
2210 break;
2211
Willy Tarreau2beef582012-12-20 17:22:52 +01002212 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002213 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002214 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002215 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002216 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002217 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002218 last_isspace = 0;
2219 break;
2220
Willy Tarreau2beef582012-12-20 17:22:52 +01002221 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002222 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002223 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002224 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002225 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002226 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002227 last_isspace = 0;
2228 break;
2229
William Lallemand1d705562012-03-12 12:46:41 +01002230 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002231 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2232 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002233 *tmplog = '\0';
2234 last_isspace = 0;
2235 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002236
William Lallemand1d705562012-03-12 12:46:41 +01002237 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002238 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2239 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002240 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2241 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 +01002242 last_isspace = 0;
2243 break;
2244
William Lallemand1d705562012-03-12 12:46:41 +01002245 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002246 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002247 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002248 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002249 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002250 last_isspace = 0;
2251 break;
2252
William Lallemand1d705562012-03-12 12:46:41 +01002253 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002254 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002255 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002256 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002257 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002258 last_isspace = 0;
2259 break;
2260
William Lallemand1d705562012-03-12 12:46:41 +01002261 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002262 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002263 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002264 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002265 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002266 last_isspace = 0;
2267 break;
2268
William Lallemand1d705562012-03-12 12:46:41 +01002269 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002270 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002271 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002272 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002273 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002274 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002275 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002276 last_isspace = 0;
2277 break;
2278
William Lallemand1d705562012-03-12 12:46:41 +01002279 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002280 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002281 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002282 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002283 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002284 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002285 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002286 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002287 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002288 last_isspace = 0;
2289 break;
2290
William Lallemand1d705562012-03-12 12:46:41 +01002291 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002292 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002293 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002294 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002295 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002296 last_isspace = 0;
2297 break;
2298
William Lallemand1d705562012-03-12 12:46:41 +01002299 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002300 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002301 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002302 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002303 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002304 last_isspace = 0;
2305 break;
2306
William Lallemand1d705562012-03-12 12:46:41 +01002307 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002308 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002309 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002310 if (tmp->options & LOG_OPT_QUOTE)
2311 LOGCHAR('"');
2312 LOGCHAR('{');
2313 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2314 if (hdr)
2315 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002316 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002317 ret = lf_encode_string(tmplog, dst + maxsize,
2318 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002319 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002320 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002321 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002322 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002323 }
2324 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002325 if (tmp->options & LOG_OPT_QUOTE)
2326 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002327 last_isspace = 0;
2328 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002329 break;
2330
William Lallemand1d705562012-03-12 12:46:41 +01002331 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002332 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002333 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002334 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2335 if (hdr > 0)
2336 LOGCHAR(' ');
2337 if (tmp->options & LOG_OPT_QUOTE)
2338 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002339 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002340 ret = lf_encode_string(tmplog, dst + maxsize,
2341 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002342 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002343 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002344 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002345 } else if (!(tmp->options & LOG_OPT_QUOTE))
2346 LOGCHAR('-');
2347 if (tmp->options & LOG_OPT_QUOTE)
2348 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002349 last_isspace = 0;
2350 }
2351 }
2352 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002353
William Lallemand1d705562012-03-12 12:46:41 +01002354
2355 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002356 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002357 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002358 if (tmp->options & LOG_OPT_QUOTE)
2359 LOGCHAR('"');
2360 LOGCHAR('{');
2361 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2362 if (hdr)
2363 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002364 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002365 ret = lf_encode_string(tmplog, dst + maxsize,
2366 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002367 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002368 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002369 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002370 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002371 }
2372 LOGCHAR('}');
2373 last_isspace = 0;
2374 if (tmp->options & LOG_OPT_QUOTE)
2375 LOGCHAR('"');
2376 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002377 break;
2378
William Lallemand1d705562012-03-12 12:46:41 +01002379 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002380 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002381 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002382 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2383 if (hdr > 0)
2384 LOGCHAR(' ');
2385 if (tmp->options & LOG_OPT_QUOTE)
2386 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002387 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002388 ret = lf_encode_string(tmplog, dst + maxsize,
2389 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002390 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002391 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002392 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002393 } else if (!(tmp->options & LOG_OPT_QUOTE))
2394 LOGCHAR('-');
2395 if (tmp->options & LOG_OPT_QUOTE)
2396 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002397 last_isspace = 0;
2398 }
2399 }
2400 break;
2401
William Lallemand1d705562012-03-12 12:46:41 +01002402 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002403 /* Request */
2404 if (tmp->options & LOG_OPT_QUOTE)
2405 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002406 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002407 ret = lf_encode_string(tmplog, dst + maxsize,
2408 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002409 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002410 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002411 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002412 if (tmp->options & LOG_OPT_QUOTE)
2413 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002414 last_isspace = 0;
2415 break;
William Lallemand5f232402012-04-05 18:02:55 +02002416
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002417 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002418 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002419
Willy Tarreaub7636d12015-06-17 19:58:02 +02002420 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002421 LOGCHAR('"');
2422
2423 end = uri + strlen(uri);
2424 // look for the first whitespace character
2425 while (uri < end && !HTTP_IS_SPHT(*uri))
2426 uri++;
2427
2428 // keep advancing past multiple spaces
2429 while (uri < end && HTTP_IS_SPHT(*uri)) {
2430 uri++; nspaces++;
2431 }
2432
2433 // look for first space or question mark after url
2434 spc = uri;
2435 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2436 spc++;
2437
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002438 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002439 chunk.area = "<BADREQ>";
2440 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002441 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002442 chunk.area = uri;
2443 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002444 }
2445
Dragan Dosen835b9212016-02-12 13:23:03 +01002446 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002447 if (ret == NULL || *ret != '\0')
2448 goto out;
2449
2450 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002451 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002452 LOGCHAR('"');
2453
2454 last_isspace = 0;
2455 break;
2456
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002457 case LOG_FMT_HTTP_QUERY: // %HQ
2458 if (tmp->options & LOG_OPT_QUOTE)
2459 LOGCHAR('"');
2460
Willy Tarreau57bc8912016-04-25 17:09:40 +02002461 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002462 chunk.area = "<BADREQ>";
2463 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002464 } else {
2465 uri = txn->uri;
2466 end = uri + strlen(uri);
2467 // look for the first question mark
2468 while (uri < end && *uri != '?')
2469 uri++;
2470
2471 qmark = uri;
2472 // look for first space or question mark after url
2473 while (uri < end && !HTTP_IS_SPHT(*uri))
2474 uri++;
2475
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002476 chunk.area = qmark;
2477 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002478 }
2479
Dragan Dosen835b9212016-02-12 13:23:03 +01002480 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002481 if (ret == NULL || *ret != '\0')
2482 goto out;
2483
2484 tmplog = ret;
2485 if (tmp->options & LOG_OPT_QUOTE)
2486 LOGCHAR('"');
2487
2488 last_isspace = 0;
2489 break;
2490
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002491 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002492 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002493
Willy Tarreaub7636d12015-06-17 19:58:02 +02002494 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002495 LOGCHAR('"');
2496
2497 end = uri + strlen(uri);
2498 // look for the first whitespace character
2499 while (uri < end && !HTTP_IS_SPHT(*uri))
2500 uri++;
2501
2502 // keep advancing past multiple spaces
2503 while (uri < end && HTTP_IS_SPHT(*uri)) {
2504 uri++; nspaces++;
2505 }
2506
2507 // look for first space after url
2508 spc = uri;
2509 while (spc < end && !HTTP_IS_SPHT(*spc))
2510 spc++;
2511
Willy Tarreau57bc8912016-04-25 17:09:40 +02002512 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002513 chunk.area = "<BADREQ>";
2514 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002515 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002516 chunk.area = uri;
2517 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002518 }
2519
Dragan Dosen835b9212016-02-12 13:23:03 +01002520 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002521 if (ret == NULL || *ret != '\0')
2522 goto out;
2523
2524 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002525 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002526 LOGCHAR('"');
2527
2528 last_isspace = 0;
2529 break;
2530
2531 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002532 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002533 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002534 LOGCHAR('"');
2535
2536 end = uri + strlen(uri);
2537 // look for the first whitespace character
2538 spc = uri;
2539 while (spc < end && !HTTP_IS_SPHT(*spc))
2540 spc++;
2541
2542 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002543 chunk.area = "<BADREQ>";
2544 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002545 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002546 chunk.area = uri;
2547 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002548 }
2549
Dragan Dosen835b9212016-02-12 13:23:03 +01002550 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002551 if (ret == NULL || *ret != '\0')
2552 goto out;
2553
2554 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002555 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002556 LOGCHAR('"');
2557
2558 last_isspace = 0;
2559 break;
2560
2561 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002562 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002563 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002564 LOGCHAR('"');
2565
2566 end = uri + strlen(uri);
2567 // look for the first whitespace character
2568 while (uri < end && !HTTP_IS_SPHT(*uri))
2569 uri++;
2570
2571 // keep advancing past multiple spaces
2572 while (uri < end && HTTP_IS_SPHT(*uri)) {
2573 uri++; nspaces++;
2574 }
2575
2576 // look for the next whitespace character
2577 while (uri < end && !HTTP_IS_SPHT(*uri))
2578 uri++;
2579
2580 // keep advancing past multiple spaces
2581 while (uri < end && HTTP_IS_SPHT(*uri))
2582 uri++;
2583
Willy Tarreau57bc8912016-04-25 17:09:40 +02002584 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002585 chunk.area = "<BADREQ>";
2586 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002587 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002588 chunk.area = "HTTP/0.9";
2589 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002590 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002591 chunk.area = uri;
2592 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002593 }
2594
Dragan Dosen835b9212016-02-12 13:23:03 +01002595 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002596 if (ret == NULL || *ret != '\0')
2597 goto out;
2598
2599 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002600 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002601 LOGCHAR('"');
2602
2603 last_isspace = 0;
2604 break;
2605
William Lallemand5f232402012-04-05 18:02:55 +02002606 case LOG_FMT_COUNTER: // %rt
2607 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002608 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002609 if (iret < 0 || iret > dst + maxsize - tmplog)
2610 goto out;
2611 last_isspace = 0;
2612 tmplog += iret;
2613 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002614 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002615 if (ret == NULL)
2616 goto out;
2617 tmplog = ret;
2618 last_isspace = 0;
2619 }
2620 break;
2621
Willy Tarreau7346acb2014-08-28 15:03:15 +02002622 case LOG_FMT_LOGCNT: // %lc
2623 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002624 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002625 if (iret < 0 || iret > dst + maxsize - tmplog)
2626 goto out;
2627 last_isspace = 0;
2628 tmplog += iret;
2629 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002630 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002631 if (ret == NULL)
2632 goto out;
2633 tmplog = ret;
2634 last_isspace = 0;
2635 }
2636 break;
2637
William Lallemand5f232402012-04-05 18:02:55 +02002638 case LOG_FMT_HOSTNAME: // %H
2639 src = hostname;
2640 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2641 if (ret == NULL)
2642 goto out;
2643 tmplog = ret;
2644 last_isspace = 0;
2645 break;
2646
2647 case LOG_FMT_PID: // %pid
2648 if (tmp->options & LOG_OPT_HEXA) {
2649 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2650 if (iret < 0 || iret > dst + maxsize - tmplog)
2651 goto out;
2652 last_isspace = 0;
2653 tmplog += iret;
2654 } else {
2655 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2656 if (ret == NULL)
2657 goto out;
2658 tmplog = ret;
2659 last_isspace = 0;
2660 }
2661 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002662
2663 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002664 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002665 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002666 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002667 if (ret == NULL)
2668 goto out;
2669 tmplog = ret;
2670 last_isspace = 0;
2671 break;
2672
William Lallemandbddd4fd2012-02-27 11:23:10 +01002673 }
2674 }
2675
2676out:
William Lallemand1d705562012-03-12 12:46:41 +01002677 /* *tmplog is a unused character */
2678 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002679 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002680
Willy Tarreaubaaee002006-06-26 02:48:02 +02002681}
2682
William Lallemand1d705562012-03-12 12:46:41 +01002683/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002684 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002685 * Will not log if the frontend has no log defined.
2686 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002687void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002688{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002689 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002690 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002691 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002692
2693 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002694 err = (s->flags & SF_REDISP) ||
2695 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2696 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002697 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002698 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002699
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002700 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002701 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002702
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002703 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002704 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002705
Willy Tarreauabcd5142013-06-11 17:18:02 +02002706 if (s->logs.level) { /* loglevel was overridden */
2707 if (s->logs.level == -1) {
2708 s->logs.logwait = 0; /* logs disabled */
2709 return;
2710 }
2711 level = s->logs.level - 1;
2712 }
2713 else {
2714 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002715 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002716 level = LOG_ERR;
2717 }
William Lallemand1d705562012-03-12 12:46:41 +01002718
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002719 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002720 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002721 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002722 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002723 }
2724
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002725 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2726 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2727 &sess->fe->logformat_sd);
2728 }
2729
Dragan Dosen59cee972015-09-19 22:09:02 +02002730 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002731 if (size > 0) {
Christopher Fauletff8abcd2017-06-02 15:33:24 +02002732 HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002733 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002734 s->logs.logwait = 0;
2735 }
2736}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002737
Willy Tarreau53839352018-09-05 19:51:10 +02002738/*
2739 * send a minimalist log for the session. Will not log if the frontend has no
2740 * log defined. It is assumed that this is only used to report anomalies that
2741 * cannot lead to the creation of a regular stream. Because of this the log
2742 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2743 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002744 * function to report unimportant events. It is safe to call this function with
2745 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002746 */
2747void sess_log(struct session *sess)
2748{
2749 int size, level;
2750 int sd_size = 0;
2751
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002752 if (!sess)
2753 return;
2754
Willy Tarreau53839352018-09-05 19:51:10 +02002755 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2756 return;
2757
2758 level = LOG_INFO;
2759 if (sess->fe->options2 & PR_O2_LOGERRORS)
2760 level = LOG_ERR;
2761
2762 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2763 sd_size = sess_build_logline(sess, NULL,
2764 logline_rfc5424, global.max_syslog_len,
2765 &sess->fe->logformat_sd);
2766 }
2767
2768 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2769 if (size > 0) {
2770 HA_ATOMIC_ADD(&sess->fe->log_count, 1);
2771 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
2772 }
2773}
2774
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002775static int cli_io_handler_show_startup_logs(struct appctx *appctx)
2776{
2777 struct stream_interface *si = appctx->owner;
2778 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
2779
2780 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreau0cd3bd62018-11-06 18:46:37 +01002781 si_cant_put(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002782 return 0;
2783 }
2784 return 1;
2785}
2786
2787/* register cli keywords */
2788static struct cli_kw_list cli_kws = {{ },{
2789 { { "show", "startup-logs", NULL },
2790 "show startup-logs : report logs emitted during HAProxy startup",
2791 NULL, cli_io_handler_show_startup_logs },
2792 {{},}
2793}};
2794
2795__attribute__((constructor))
2796static void __log_init(void)
2797{
Christopher Fauletcd7879a2017-10-27 13:53:47 +02002798 hap_register_per_thread_init(init_log_buffers_per_thread);
2799 hap_register_per_thread_deinit(deinit_log_buffers_per_thread);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002800 cli_register_kw(&cli_kws);
2801}
Willy Tarreaubaaee002006-06-26 02:48:02 +02002802/*
2803 * Local variables:
2804 * c-indent-level: 8
2805 * c-basic-offset: 8
2806 * End:
2807 */