blob: 50553c6fbeb4e3b32aa3201a57ac2e13f9eb31a2 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * General logging functions.
3 *
Willy Tarreaub7f694f2008-06-22 17:18:02 +02004 * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau8a3f52f2012-12-20 21:23:42 +010013#include <ctype.h>
Willy Tarreauc8f24f82007-11-30 18:38:35 +010014#include <fcntl.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020015#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <syslog.h>
20#include <time.h>
21#include <unistd.h>
Robert Tsai81ae1952007-12-05 10:47:29 +010022#include <errno.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023
24#include <sys/time.h>
Willy Tarreau077edcb2016-08-10 18:30:56 +020025#include <sys/uio.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
Willy Tarreaue3ba5f02006-06-29 18:54:54 +020027#include <common/config.h>
Willy Tarreaud6d06902009-08-19 11:22:33 +020028#include <common/compat.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010029#include <common/initcall.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020030#include <common/standard.h>
Willy Tarreaufb278672006-10-15 15:38:50 +020031#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020032
Christopher Fauletc1b730a2017-10-24 12:00:51 +020033#include <types/cli.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020034#include <types/global.h>
William Lallemand723b73a2012-02-08 16:37:49 +010035#include <types/log.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020036
Christopher Fauletc1b730a2017-10-24 12:00:51 +020037#include <proto/applet.h>
38#include <proto/cli.h>
William Lallemand5f232402012-04-05 18:02:55 +020039#include <proto/frontend.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020040#include <proto/log.h>
Willy Tarreauc8368452012-12-21 00:09:23 +010041#include <proto/sample.h>
Willy Tarreauc125cef2019-05-10 09:58:43 +020042#include <proto/ssl_sock.h>
Willy Tarreaufb0afa72015-04-03 14:46:27 +020043#include <proto/stream.h>
Willy Tarreau827aee92011-03-10 16:55:02 +010044#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020045
Dragan Dosen43885c72015-10-01 13:18:13 +020046struct log_fmt {
47 char *name;
48 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020049 struct buffer sep1; /* first pid separator */
50 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020051 } pid;
52};
53
54static const struct log_fmt log_formats[LOG_FORMATS] = {
55 [LOG_FORMAT_RFC3164] = {
56 .name = "rfc3164",
57 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020058 .sep1 = { .area = "[", .data = 1 },
59 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020060 }
61 },
62 [LOG_FORMAT_RFC5424] = {
63 .name = "rfc5424",
64 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020065 .sep1 = { .area = " ", .data = 1 },
66 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020067 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010068 },
69 [LOG_FORMAT_SHORT] = {
70 .name = "short",
71 .pid = {
72 .sep1 = { .area = "", .data = 0 },
73 .sep2 = { .area = " ", .data = 1 },
74 }
75 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010076 [LOG_FORMAT_RAW] = {
77 .name = "raw",
78 .pid = {
79 .sep1 = { .area = "", .data = 0 },
80 .sep2 = { .area = "", .data = 0 },
81 }
82 },
Dragan Dosen1322d092015-09-22 16:05:32 +020083};
84
Dragan Dosen835b9212016-02-12 13:23:03 +010085#define FD_SETS_ARE_BITFIELDS
86#ifdef FD_SETS_ARE_BITFIELDS
87/*
88 * This map is used with all the FD_* macros to check whether a particular bit
89 * is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
90 * which should be escaped. When FD_ISSET() returns non-zero, it means that the
91 * byte should be escaped. Be careful to always pass bytes from 0 to 255
92 * exclusively to the macros.
93 */
94fd_set rfc5424_escape_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
Willy Tarreaue10cd482018-09-10 18:16:53 +020095fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
96fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
97fd_set http_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
Dragan Dosen835b9212016-02-12 13:23:03 +010098
99#else
100#error "Check if your OS uses bitfields for fd_sets"
101#endif
102
Willy Tarreaubaaee002006-06-26 02:48:02 +0200103const char *log_facilities[NB_LOG_FACILITIES] = {
104 "kern", "user", "mail", "daemon",
105 "auth", "syslog", "lpr", "news",
106 "uucp", "cron", "auth2", "ftp",
107 "ntp", "audit", "alert", "cron2",
108 "local0", "local1", "local2", "local3",
109 "local4", "local5", "local6", "local7"
110};
111
Willy Tarreaubaaee002006-06-26 02:48:02 +0200112const char *log_levels[NB_LOG_LEVELS] = {
113 "emerg", "alert", "crit", "err",
114 "warning", "notice", "info", "debug"
115};
116
Willy Tarreau570f2212013-06-10 16:42:09 +0200117const 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 +0200118const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200119
William Lallemand723b73a2012-02-08 16:37:49 +0100120
121/* log_format */
122struct logformat_type {
123 char *name;
124 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100125 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200126 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100127 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
Willy Tarreau2beef582012-12-20 17:22:52 +0100128 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100129};
130
William Lallemandb7ff6a32012-03-02 14:35:21 +0100131int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
132
William Lallemand723b73a2012-02-08 16:37:49 +0100133/* log_format variable names */
134static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200135 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100136
137 /* please keep these lines sorted ! */
138 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
139 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
140 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
141 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
142 { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200143 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200144 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200145 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100146 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200147 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
148 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
149 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
150 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
151 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
152 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200153 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100154 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200155 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Willy Tarreau2beef582012-12-20 17:22:52 +0100156 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
157 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200158 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100159 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200160 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100161 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
162 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200163 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200164 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
165 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100166 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
167 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200168 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
169 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100170 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200171 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
172 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
173 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
174 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000175 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
176 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000177 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000178 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
179 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200180 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100181 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200182 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100183 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
184 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100185 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100186 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
187 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
188 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
189 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
190 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200191 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
192 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100193 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200194 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
195 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
196 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100197 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
198 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
199
200 /* The following tags are deprecated and will be removed soon */
201 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
202 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200203 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
204 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
205 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
206 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100207 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
208 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
209 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
210 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
211 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200212 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100213};
214
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200215char 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
216char 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 +0100217char 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 +0100218char *log_format = NULL;
219
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200220/* Default string used for structured-data part in RFC5424 formatted
221 * syslog messages.
222 */
223char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200224
Willy Tarreau13ef7732018-11-12 07:25:28 +0100225/* total number of dropped logs */
226unsigned int dropped_logs = 0;
227
Dragan Dosen1322d092015-09-22 16:05:32 +0200228/* This is a global syslog header, common to all outgoing messages in
229 * RFC3164 format. It begins with time-based part and is updated by
230 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200231 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200232THREAD_LOCAL char *logheader = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200233THREAD_LOCAL char *logheader_end = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200234
Dragan Dosen1322d092015-09-22 16:05:32 +0200235/* This is a global syslog header for messages in RFC5424 format. It is
236 * updated by update_log_hdr_rfc5424().
237 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200238THREAD_LOCAL char *logheader_rfc5424 = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200239THREAD_LOCAL char *logheader_rfc5424_end = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200240
Dragan Dosen59cee972015-09-19 22:09:02 +0200241/* This is a global syslog message buffer, common to all outgoing
242 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100243 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200244THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100245
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200246/* A global syslog message buffer, common to all RFC5424 syslog messages.
247 * Currently, it is used for generating the structured-data part.
248 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200249THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200250
Christopher Fauletd4696382017-10-24 11:44:05 +0200251/* A global buffer used to store all startup alerts/warnings. It will then be
252 * retrieve on the CLI. */
Willy Tarreaua6483992018-12-15 16:55:36 +0100253static char *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200254
William Lallemand723b73a2012-02-08 16:37:49 +0100255struct logformat_var_args {
256 char *name;
257 int mask;
258};
259
260struct logformat_var_args var_args_list[] = {
261// global
262 { "M", LOG_OPT_MANDATORY },
263 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200264 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100265 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100266 { 0, 0 }
267};
268
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200269/* return the name of the directive used in the current proxy for which we're
270 * currently parsing a header, when it is known.
271 */
272static inline const char *fmt_directive(const struct proxy *curproxy)
273{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100274 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200275 case ARGC_ACL:
276 return "acl";
277 case ARGC_STK:
278 return "stick";
279 case ARGC_TRK:
280 return "track-sc";
281 case ARGC_LOG:
282 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200283 case ARGC_LOGSD:
284 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100285 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100286 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100287 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100288 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200289 case ARGC_UIF:
290 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100291 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200292 return "redirect";
293 case ARGC_CAP:
294 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200295 case ARGC_SRV:
296 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200297 case ARGC_SPOE:
298 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100299 case ARGC_UBK:
300 return "use_backend";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100301 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200302 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100303 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200304}
305
William Lallemand723b73a2012-02-08 16:37:49 +0100306/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100307 * callback used to configure addr source retrieval
308 */
309int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
310{
311 curproxy->options2 |= PR_O2_SRC_ADDR;
312
313 return 0;
314}
315
316
317/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100318 * Parse args in a logformat_var. Returns 0 in error
319 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100320 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100321int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100322{
323 int i = 0;
324 int end = 0;
325 int flags = 0; // 1 = + 2 = -
326 char *sp = NULL; // start pointer
327
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100328 if (args == NULL) {
329 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100330 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100331 }
William Lallemand723b73a2012-02-08 16:37:49 +0100332
333 while (1) {
334 if (*args == '\0')
335 end = 1;
336
337 if (*args == '+') {
338 // add flag
339 sp = args + 1;
340 flags = 1;
341 }
342 if (*args == '-') {
343 // delete flag
344 sp = args + 1;
345 flags = 2;
346 }
347
348 if (*args == '\0' || *args == ',') {
349 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100350 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100351 if (strcmp(sp, var_args_list[i].name) == 0) {
352 if (flags == 1) {
353 node->options |= var_args_list[i].mask;
354 break;
355 } else if (flags == 2) {
356 node->options &= ~var_args_list[i].mask;
357 break;
358 }
359 }
360 }
361 sp = NULL;
362 if (end)
363 break;
364 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100365 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100366 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100367 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100368}
369
370/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100371 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
372 * must pass the args part in the <arg> pointer with its length in <arg_len>,
373 * and varname with its length in <var> and <var_len> respectively. <arg> is
374 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100375 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100376 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100377int parse_logformat_var(char *arg, int arg_len, char *var, int var_len, struct proxy *curproxy, struct list *list_format, int *defoptions, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100378{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100379 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200380 struct logformat_node *node = NULL;
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");
Dragan Dosen61302da2019-04-30 00:40:02 +0200389 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100390 }
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))
Dragan Dosen61302da2019-04-30 00:40:02 +0200396 goto error_free;
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) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200405 goto error_free;
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);
Dragan Dosen61302da2019-04-30 00:40:02 +0200418 goto error_free;
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;
Dragan Dosen61302da2019-04-30 00:40:02 +0200427
428 error_free:
429 if (node) {
430 free(node->arg);
431 free(node);
432 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100433 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100434}
435
436/*
437 * push to the logformat linked list
438 *
439 * start: start pointer
440 * end: end text pointer
441 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100442 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100443 *
444 * LOG_TEXT: copy chars from start to end excluding end.
445 *
446*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100447int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100448{
449 char *str;
450
Willy Tarreaua3571662012-12-20 21:59:12 +0100451 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200452 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100453 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100454 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100455 return 0;
456 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200457 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100458 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100459 str[end - start] = '\0';
460 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100461 node->type = LOG_FMT_TEXT; // type string
462 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100463 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200464 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100465 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100466 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100467 return 0;
468 }
William Lallemand1d705562012-03-12 12:46:41 +0100469 node->type = LOG_FMT_SEPARATOR;
470 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100471 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100472 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100473}
474
475/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100476 * Parse the sample fetch expression <text> and add a node to <list_format> upon
477 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200478 * should work. The curpx->conf.args.ctx must be set by the caller.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100479 *
480 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100481 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100482int 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 +0100483{
484 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200485 struct sample_expr *expr = NULL;
486 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100487 int cmd_arg;
488
489 cmd[0] = text;
490 cmd[1] = "";
491 cmd_arg = 0;
492
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100493 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 +0100494 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100495 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200496 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100497 }
498
Vincent Bernat02779b62016-04-03 13:48:43 +0200499 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100500 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100501 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200502 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100503 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100504 node->type = LOG_FMT_EXPR;
505 node->expr = expr;
506 node->options = options;
507
508 if (arg_len) {
509 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100510 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200511 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100512 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100513 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100514 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
515
Willy Tarreau434c57c2013-01-08 01:10:24 +0100516 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100517 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
518
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100519 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100520 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
521 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200522 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100523 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100524
Willy Tarreauc8368452012-12-21 00:09:23 +0100525 /* check if we need to allocate an hdr_idx struct for HTTP parsing */
526 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100527 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100528
William Lallemand65ad6e12014-01-31 15:08:02 +0100529 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
530 * needed with some sample fetches (eg: ssl*). We always set it for
531 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100532 */
533 curpx->to_log |= LW_XPRT;
William Lallemand65ad6e12014-01-31 15:08:02 +0100534 curpx->to_log |= LW_REQ;
Willy Tarreauc8368452012-12-21 00:09:23 +0100535 LIST_ADDQ(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100536 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200537
538 error_free:
539 release_sample_expr(expr);
540 if (node) {
541 free(node->arg);
542 free(node);
543 }
544 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100545}
546
547/*
William Lallemand723b73a2012-02-08 16:37:49 +0100548 * Parse the log_format string and fill a linked list.
549 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200550 * You can set arguments using { } : %{many arguments}varname.
551 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100552 *
553 * str: the string to parse
554 * curproxy: the proxy affected
555 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100556 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100557 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100558 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100559 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100560 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100561int 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 +0100562{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100563 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100564 char *arg = NULL; /* start pointer for args */
565 char *var = NULL; /* start pointer for vars */
566 int arg_len = 0;
567 int var_len = 0;
568 int cformat; /* current token format */
569 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100570 struct logformat_node *tmplf, *back;
571
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100572 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100573 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100574 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100575 return 0;
576 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200577 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200578
William Lallemand723b73a2012-02-08 16:37:49 +0100579 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100580 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100581 LIST_DEL(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200582 release_sample_expr(tmplf->expr);
583 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100584 free(tmplf);
585 }
586
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100587 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100588 pformat = cformat;
589
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100590 if (!*str)
591 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100592
Joseph Herlant85b40592018-11-15 12:10:04 -0800593 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100594 * second have all common paths processed at one place. The common paths are the ones
595 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
596 * We use the common LF_INIT state to dispatch to the different final states.
597 */
598 switch (pformat) {
599 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100600 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100601 arg_len = var_len = 0;
602 if (*str == '{') { // optional argument
603 cformat = LF_STARG;
604 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100605 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100606 else if (*str == '[') {
607 cformat = LF_STEXPR;
608 var = str + 1; // store expr in variable name
609 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100610 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100611 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100612 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100613 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100614 else if (*str == '%')
615 cformat = LF_TEXT; // convert this character to a litteral (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100616 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100617 /* single '%' followed by blank or digit, send them both */
618 cformat = LF_TEXT;
619 pformat = LF_TEXT; /* finally we include the previous char as well */
620 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600621 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 +0100622 *str, (int)(str - backfmt), fmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100623 return 0;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100624
625 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100626 else
627 cformat = LF_INIT; // handle other cases of litterals
628 break;
629
630 case LF_STARG: // text immediately following '%{'
631 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100632 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100633 arg_len = str - arg;
634 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100635 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100636 break;
637
638 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100639 if (*str == '[') {
640 cformat = LF_STEXPR;
641 var = str + 1; // store expr in variable name
642 break;
643 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100644 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100645 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100646 var = str;
647 break;
648 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100649 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100650 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100651
Willy Tarreauc8368452012-12-21 00:09:23 +0100652 case LF_STEXPR: // text immediately following '%['
653 if (*str == ']') { // end of arg
654 cformat = LF_EDEXPR;
655 var_len = str - var;
656 *str = 0; // needed for parsing the expression
657 }
658 break;
659
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100660 case LF_VAR: // text part of a variable name
661 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100662 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100663 cformat = LF_INIT; // not variable name anymore
664 break;
665
Willy Tarreauc8368452012-12-21 00:09:23 +0100666 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100667 cformat = LF_INIT;
668 }
669
670 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
671 switch (*str) {
672 case '%': cformat = LF_STARTVAR; break;
673 case ' ': cformat = LF_SEPARATOR; break;
674 case 0 : cformat = LF_END; break;
675 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100676 }
677 }
678
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100679 if (cformat != pformat || pformat == LF_SEPARATOR) {
680 switch (pformat) {
681 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100682 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100683 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100684 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100685 case LF_STEXPR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100686 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 +0100687 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100688 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100689 case LF_TEXT:
690 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100691 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100692 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100693 break;
694 }
695 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100696 }
697 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100698
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100699 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100700 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100701 return 0;
702 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100703 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100704
705 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100706}
707
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200708/*
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200709 * Parse the first range of indexes from a string made of a list of comma seperated
710 * ranges of indexes. Note that an index may be considered as a particular range
711 * with a high limit to the low limit.
712 */
713int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
714{
715 char *end, *p;
716
717 *low = *high = 0;
718
719 p = *arg;
720 end = strchr(p, ',');
721 if (!end)
722 end = p + strlen(p);
723
724 *high = *low = read_uint((const char **)&p, end);
725 if (!*low || (p != end && *p != '-'))
726 goto err;
727
728 if (p == end)
729 goto done;
730
731 p++;
732 *high = read_uint((const char **)&p, end);
733 if (!*high || *high <= *low || p != end)
734 goto err;
735
736 done:
737 if (*end == ',')
738 end++;
739 *arg = end;
740 return 1;
741
742 err:
743 memprintf(err, "wrong sample range '%s'", *arg);
744 return 0;
745}
746
747/*
748 * Returns 1 if the range defined by <low> and <high> overlaps
749 * one of them in <rgs> array of ranges with <sz> the size of this
750 * array, 0 if not.
751 */
752int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
753 unsigned int low, unsigned int high, char **err)
754{
755 size_t i;
756
757 for (i = 0; i < sz; i++) {
758 if ((low >= rgs[i].low && low <= rgs[i].high) ||
759 (high >= rgs[i].low && high <= rgs[i].high)) {
760 memprintf(err, "ranges are overlapping");
761 return 1;
762 }
763 }
764
765 return 0;
766}
767
768int smp_log_range_cmp(const void *a, const void *b)
769{
770 const struct smp_log_range *rg_a = a;
771 const struct smp_log_range *rg_b = b;
772
773 if (rg_a->high < rg_b->low)
774 return -1;
775 else if (rg_a->low > rg_b->high)
776 return 1;
777
778 return 0;
779}
780
781/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200782 * Parse "log" keyword and update <logsrvs> list accordingly.
783 *
784 * When <do_del> is set, it means the "no log" line was parsed, so all log
785 * servers in <logsrvs> are released.
786 *
787 * Otherwise, we try to parse the "log" line. First of all, when the list is not
788 * the global one, we look for the parameter "global". If we find it,
789 * global.logsrvs is copied. Else we parse each arguments.
790 *
791 * The function returns 1 in success case, otherwise, it returns 0 and err is
792 * filled.
793 */
794int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
795{
796 struct sockaddr_storage *sk;
797 struct logsrv *logsrv = NULL;
798 int port1, port2;
799 int cur_arg;
800
801 /*
802 * "no log": delete previous herited or defined syslog
803 * servers.
804 */
805 if (do_del) {
806 struct logsrv *back;
807
808 if (*(args[1]) != 0) {
809 memprintf(err, "'no log' does not expect arguments");
810 goto error;
811 }
812
813 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
814 LIST_DEL(&logsrv->list);
815 free(logsrv);
816 }
817 return 1;
818 }
819
820 /*
821 * "log global": copy global.logrsvs linked list to the end of logsrvs
822 * list. But first, we check (logsrvs != global.logsrvs).
823 */
824 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
825 if (logsrvs == &global.logsrvs) {
826 memprintf(err, "'global' is not supported for a global syslog server");
827 goto error;
828 }
829 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200830 struct logsrv *node;
831
832 list_for_each_entry(node, logsrvs, list) {
833 if (node->ref == logsrv)
834 goto skip_logsrv;
835 }
836
837 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200838 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200839 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200840 LIST_INIT(&node->list);
841 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200842
843 skip_logsrv:
844 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200845 }
846 return 1;
847 }
848
849 /*
850 * "log <address> ...: parse a syslog server line
851 */
852 if (*(args[1]) == 0 || *(args[2]) == 0) {
853 memprintf(err, "expects <address> and <facility> %s as arguments",
854 ((logsrvs == &global.logsrvs) ? "" : "or global"));
855 goto error;
856 }
857
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100858 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
859 if (strcmp(args[1], "stdout") == 0)
860 args[1] = "fd@1";
861 else if (strcmp(args[1], "stderr") == 0)
862 args[1] = "fd@2";
863
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200864 logsrv = calloc(1, sizeof(*logsrv));
865 if (!logsrv) {
866 memprintf(err, "out of memory");
867 goto error;
868 }
869
870 /* skip address for now, it will be parsed at the end */
871 cur_arg = 2;
872
873 /* just after the address, a length may be specified */
874 logsrv->maxlen = MAX_SYSLOG_LEN;
875 if (strcmp(args[cur_arg], "len") == 0) {
876 int len = atoi(args[cur_arg+1]);
877 if (len < 80 || len > 65535) {
878 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
879 args[cur_arg+1]);
880 goto error;
881 }
882 logsrv->maxlen = len;
883 cur_arg += 2;
884 }
885 if (logsrv->maxlen > global.max_syslog_len)
886 global.max_syslog_len = logsrv->maxlen;
887
888 /* after the length, a format may be specified */
889 if (strcmp(args[cur_arg], "format") == 0) {
890 logsrv->format = get_log_format(args[cur_arg+1]);
891 if (logsrv->format < 0) {
892 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
893 goto error;
894 }
895 cur_arg += 2;
896 }
897
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200898 if (strcmp(args[cur_arg], "sample") == 0) {
899 unsigned low, high;
900 char *p, *beg, *end, *smp_sz_str;
901 struct smp_log_range *smp_rgs = NULL;
902 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
903
904 p = args[cur_arg+1];
905 smp_sz_str = strchr(p, ':');
906 if (!smp_sz_str) {
907 memprintf(err, "Missing sample size");
908 goto error;
909 }
910
911 *smp_sz_str++ = '\0';
912
913 end = p + strlen(p);
914
915 while (p != end) {
916 if (!get_logsrv_smp_range(&low, &high, &p, err))
917 goto error;
918
919 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
920 goto error;
921
922 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
923 if (!smp_rgs) {
924 memprintf(err, "out of memory error");
925 goto error;
926 }
927
928 smp_rgs[smp_rgs_sz].low = low;
929 smp_rgs[smp_rgs_sz].high = high;
930 smp_rgs[smp_rgs_sz].sz = high - low + 1;
931 smp_rgs[smp_rgs_sz].curr_idx = 0;
932 if (smp_rgs[smp_rgs_sz].high > smp_sz)
933 smp_sz = smp_rgs[smp_rgs_sz].high;
934 smp_rgs_sz++;
935 }
936
937 beg = smp_sz_str;
938 end = beg + strlen(beg);
939 new_smp_sz = read_uint((const char **)&beg, end);
940 if (!new_smp_sz || beg != end) {
941 memprintf(err, "wrong sample size '%s' for sample range '%s'",
942 smp_sz_str, args[cur_arg+1]);
943 goto error;
944 }
945
946 if (new_smp_sz < smp_sz) {
947 memprintf(err, "sample size %zu should be greater or equal to "
948 "%zu the maximum of the high ranges limits",
949 new_smp_sz, smp_sz);
950 goto error;
951 }
952 smp_sz = new_smp_sz;
953
954 /* Let's order <smp_rgs> array. */
955 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
956
957 logsrv->lb.smp_rgs = smp_rgs;
958 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
959 logsrv->lb.smp_sz = smp_sz;
960
961 cur_arg += 2;
962 }
Frédéric Lécailled803e472019-04-25 07:42:09 +0200963 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200964 /* parse the facility */
965 logsrv->facility = get_log_facility(args[cur_arg]);
966 if (logsrv->facility < 0) {
967 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
968 goto error;
969 }
970 cur_arg++;
971
972 /* parse the max syslog level (default: debug) */
973 logsrv->level = 7;
974 if (*(args[cur_arg])) {
975 logsrv->level = get_log_level(args[cur_arg]);
976 if (logsrv->level < 0) {
977 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
978 goto error;
979 }
980 cur_arg++;
981 }
982
983 /* parse the limit syslog level (default: emerg) */
984 logsrv->minlvl = 0;
985 if (*(args[cur_arg])) {
986 logsrv->minlvl = get_log_level(args[cur_arg]);
987 if (logsrv->minlvl < 0) {
988 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
989 goto error;
990 }
991 cur_arg++;
992 }
993
994 /* Too many args */
995 if (*(args[cur_arg])) {
996 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
997 goto error;
998 }
999
1000 /* now, back to the address */
1001 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1002 if (!sk)
1003 goto error;
1004 logsrv->addr = *sk;
1005
1006 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1007 if (port1 != port2) {
1008 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1009 goto error;
1010 }
1011 logsrv->addr = *sk;
1012 if (!port1)
1013 set_host_port(&logsrv->addr, SYSLOG_PORT);
1014 }
1015 LIST_ADDQ(logsrvs, &logsrv->list);
1016 return 1;
1017
1018 error:
1019 free(logsrv);
1020 return 0;
1021}
1022
1023
Christopher Fauletd4696382017-10-24 11:44:05 +02001024/* Generic function to display messages prefixed by a label */
1025static void print_message(const char *label, const char *fmt, va_list argp)
1026{
1027 struct tm tm;
1028 char *head, *msg;
1029
1030 head = msg = NULL;
1031
1032 get_localtime(date.tv_sec, &tm);
1033 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1034 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1035 memvprintf(&msg, fmt, argp);
1036
1037 if (global.mode & MODE_STARTING)
1038 memprintf(&startup_logs, "%s%s%s", (startup_logs ? startup_logs : ""), head, msg);
1039
1040 fprintf(stderr, "%s%s", head, msg);
1041 fflush(stderr);
1042
1043 free(head);
1044 free(msg);
1045}
1046
Willy Tarreaubaaee002006-06-26 02:48:02 +02001047/*
1048 * Displays the message on stderr with the date and pid. Overrides the quiet
1049 * mode during startup.
1050 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001051void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001052{
1053 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001054
1055 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1056 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001057 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001058 va_end(argp);
1059 }
1060}
1061
1062
1063/*
1064 * Displays the message on stderr with the date and pid.
1065 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001066void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001067{
1068 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001069
1070 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1071 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001072 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001073 va_end(argp);
1074 }
1075}
1076
1077/*
William Lallemand9c56a222018-11-21 18:04:52 +01001078 * Displays the message on stderr with the date and pid.
1079 */
1080void ha_notice(const char *fmt, ...)
1081{
1082 va_list argp;
1083
1084 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1085 va_start(argp, fmt);
1086 print_message("NOTICE", fmt, argp);
1087 va_end(argp);
1088 }
1089}
1090
1091/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001092 * Displays the message on <out> only if quiet mode is not set.
1093 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001094void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001095{
1096 va_list argp;
1097
1098 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1099 va_start(argp, fmt);
1100 vfprintf(out, fmt, argp);
1101 fflush(out);
1102 va_end(argp);
1103 }
1104}
1105
1106/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001107 * returns log format for <fmt> or -1 if not found.
1108 */
1109int get_log_format(const char *fmt)
1110{
1111 int format;
1112
1113 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001114 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001115 format--;
1116
1117 return format;
1118}
1119
1120/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001121 * returns log level for <lev> or -1 if not found.
1122 */
1123int get_log_level(const char *lev)
1124{
1125 int level;
1126
1127 level = NB_LOG_LEVELS - 1;
1128 while (level >= 0 && strcmp(log_levels[level], lev))
1129 level--;
1130
1131 return level;
1132}
1133
Willy Tarreaubaaee002006-06-26 02:48:02 +02001134/*
1135 * returns log facility for <fac> or -1 if not found.
1136 */
1137int get_log_facility(const char *fac)
1138{
1139 int facility;
1140
1141 facility = NB_LOG_FACILITIES - 1;
1142 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1143 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001144
Willy Tarreaubaaee002006-06-26 02:48:02 +02001145 return facility;
1146}
1147
William Lallemanda1cc3812012-02-08 16:38:44 +01001148/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001149 * Encode the string.
1150 *
1151 * When using the +E log format option, it will try to escape '"\]'
1152 * characters with '\' as prefix. The same prefix should not be used as
1153 * <escape>.
1154 */
1155static char *lf_encode_string(char *start, char *stop,
1156 const char escape, const fd_set *map,
1157 const char *string,
1158 struct logformat_node *node)
1159{
1160 if (node->options & LOG_OPT_ESC) {
1161 if (start < stop) {
1162 stop--; /* reserve one byte for the final '\0' */
1163 while (start < stop && *string != '\0') {
1164 if (!FD_ISSET((unsigned char)(*string), map)) {
1165 if (!FD_ISSET((unsigned char)(*string), rfc5424_escape_map))
1166 *start++ = *string;
1167 else {
1168 if (start + 2 >= stop)
1169 break;
1170 *start++ = '\\';
1171 *start++ = *string;
1172 }
1173 }
1174 else {
1175 if (start + 3 >= stop)
1176 break;
1177 *start++ = escape;
1178 *start++ = hextab[(*string >> 4) & 15];
1179 *start++ = hextab[*string & 15];
1180 }
1181 string++;
1182 }
1183 *start = '\0';
1184 }
1185 }
1186 else {
1187 return encode_string(start, stop, escape, map, string);
1188 }
1189
1190 return start;
1191}
1192
1193/*
1194 * Encode the chunk.
1195 *
1196 * When using the +E log format option, it will try to escape '"\]'
1197 * characters with '\' as prefix. The same prefix should not be used as
1198 * <escape>.
1199 */
1200static char *lf_encode_chunk(char *start, char *stop,
1201 const char escape, const fd_set *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001202 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001203 struct logformat_node *node)
1204{
1205 char *str, *end;
1206
1207 if (node->options & LOG_OPT_ESC) {
1208 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001209 str = chunk->area;
1210 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001211
1212 stop--; /* reserve one byte for the final '\0' */
1213 while (start < stop && str < end) {
1214 if (!FD_ISSET((unsigned char)(*str), map)) {
1215 if (!FD_ISSET((unsigned char)(*str), rfc5424_escape_map))
1216 *start++ = *str;
1217 else {
1218 if (start + 2 >= stop)
1219 break;
1220 *start++ = '\\';
1221 *start++ = *str;
1222 }
1223 }
1224 else {
1225 if (start + 3 >= stop)
1226 break;
1227 *start++ = escape;
1228 *start++ = hextab[(*str >> 4) & 15];
1229 *start++ = hextab[*str & 15];
1230 }
1231 str++;
1232 }
1233 *start = '\0';
1234 }
1235 }
1236 else {
1237 return encode_chunk(start, stop, escape, map, chunk);
1238 }
1239
1240 return start;
1241}
1242
1243/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001244 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001245 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001246 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001247 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001248 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001249char *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 +01001250{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001251 if (size < 2)
1252 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001253
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001254 if (node->options & LOG_OPT_QUOTE) {
1255 *(dst++) = '"';
1256 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001257 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001258
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001259 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001260 if (++len > size)
1261 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001262 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001263 char *ret;
1264
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001265 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001266 if (ret == NULL || *ret != '\0')
1267 return NULL;
1268 len = ret - dst;
1269 }
1270 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001271 len = strlcpy2(dst, src, len);
1272 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001273
1274 size -= len;
1275 dst += len;
1276 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001277 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1278 if (size < 2)
1279 return NULL;
1280 *(dst++) = '-';
1281 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001282
1283 if (node->options & LOG_OPT_QUOTE) {
1284 if (size < 2)
1285 return NULL;
1286 *(dst++) = '"';
1287 }
1288
1289 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001290 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001291}
1292
Willy Tarreau26ffa852018-09-05 15:23:10 +02001293static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001294{
1295 return lf_text_len(dst, src, size, size, node);
1296}
1297
William Lallemand5f232402012-04-05 18:02:55 +02001298/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001299 * Write a IP address to the log string
William Lallemand5f232402012-04-05 18:02:55 +02001300 * +X option write in hexadecimal notation, most signifant byte on the left
1301 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001302char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001303{
1304 char *ret = dst;
1305 int iret;
1306 char pn[INET6_ADDRSTRLEN];
1307
1308 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001309 unsigned char *addr = NULL;
1310 switch (sockaddr->sa_family) {
1311 case AF_INET:
1312 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1313 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1314 break;
1315 case AF_INET6:
1316 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1317 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1318 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1319 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1320 break;
1321 default:
1322 return NULL;
1323 }
William Lallemand5f232402012-04-05 18:02:55 +02001324 if (iret < 0 || iret > size)
1325 return NULL;
1326 ret += iret;
1327 } else {
1328 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1329 ret = lf_text(dst, pn, size, node);
1330 if (ret == NULL)
1331 return NULL;
1332 }
1333 return ret;
1334}
1335
1336/*
1337 * Write a port to the log
1338 * +X option write in hexadecimal notation, most signifant byte on the left
1339 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001340char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001341{
1342 char *ret = dst;
1343 int iret;
1344
1345 if (node->options & LOG_OPT_HEXA) {
1346 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1347 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1348 if (iret < 0 || iret > size)
1349 return NULL;
1350 ret += iret;
1351 } else {
1352 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1353 if (ret == NULL)
1354 return NULL;
1355 }
1356 return ret;
1357}
1358
Dragan Dosen1322d092015-09-22 16:05:32 +02001359/* Re-generate time-based part of the syslog header in RFC3164 format at
1360 * the beginning of logheader once a second and return the pointer to the
1361 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001362 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001363static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001364{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001365 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001366 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001367 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001368
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001369 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001370 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001371 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001372 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001373
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001374 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001375 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001376
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001377 if (unlikely(global.log_send_hostname != host.area)) {
1378 host.area = global.log_send_hostname;
1379 host.data = host.area ? strlen(host.area) : 0;
1380 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001381 }
1382
Dragan Dosen59cee972015-09-19 22:09:02 +02001383 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001384 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001385 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001386 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001387 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001388 /* WARNING: depending upon implementations, snprintf may return
1389 * either -1 or the number of bytes that would be needed to store
1390 * the total message. In both cases, we must adjust it.
1391 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001392 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1393 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001394
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001395 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001396 }
1397
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001398 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001399
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001400 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001401}
1402
Dragan Dosen1322d092015-09-22 16:05:32 +02001403/* Re-generate time-based part of the syslog header in RFC5424 format at
1404 * the beginning of logheader_rfc5424 once a second and return the pointer
1405 * to the first character after it.
1406 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001407static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001408{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001409 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001410 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001411
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001412 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001413 /* this string is rebuild only once a second */
1414 struct tm tm;
1415 int hdr_len;
1416
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001417 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001418 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001419 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001420
1421 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001422 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001423 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001424 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001425 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001426 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001427 /* WARNING: depending upon implementations, snprintf may return
1428 * either -1 or the number of bytes that would be needed to store
1429 * the total message. In both cases, we must adjust it.
1430 */
1431 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1432 hdr_len = global.max_syslog_len;
1433
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001434 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001435 }
1436
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001437 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001438
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001439 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001440}
1441
William Lallemand2a4a44f2012-02-06 16:00:33 +01001442/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001443 * This function sends the syslog message using a printf format string. It
1444 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001445 */
1446void send_log(struct proxy *p, int level, const char *format, ...)
1447{
1448 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001449 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001450
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001451 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001452 return;
1453
William Lallemand2a4a44f2012-02-06 16:00:33 +01001454 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001455 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001456 if (data_len < 0 || data_len > global.max_syslog_len)
1457 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001458 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001459
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001460 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001461}
1462
1463/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001464 * This function sends a syslog message to <logsrv>.
1465 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1466 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1467 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001468 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001469 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001470 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001471static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1472 int level, char *message, size_t size, char *sd, size_t sd_size,
1473 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001474{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001475 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1476 static THREAD_LOCAL struct msghdr msghdr = {
1477 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001478 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1479 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001480 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1481 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1482 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001483 time_t time = date.tv_sec;
Dragan Dosen1322d092015-09-22 16:05:32 +02001484 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001485 size_t hdr_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001486 int fac_level;
1487 int *plogfd;
1488 char *pid_sep1 = "", *pid_sep2 = "";
1489 char logheader_short[3];
1490 int sent;
1491 int maxlen;
1492 int hdr_max = 0;
1493 int tag_max = 0;
1494 int pid_sep1_max = 0;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001495 int pid_max = 0;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001496 int pid_sep2_max = 0;
1497 int sd_max = 0;
1498 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001499
1500 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001501
1502 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001503
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001504 if (logsrv->addr.ss_family == AF_UNSPEC) {
1505 /* the socket's address is a file descriptor */
1506 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1507 if (unlikely(!((struct sockaddr_in *)&logsrv->addr)->sin_port)) {
1508 /* FD not yet initialized to non-blocking mode.
1509 * DON'T DO IT ON A TERMINAL!
1510 */
1511 if (!isatty(*plogfd))
1512 fcntl(*plogfd, F_SETFL, O_NONBLOCK);
1513 ((struct sockaddr_in *)&logsrv->addr)->sin_port = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001514 }
Robert Tsai81ae1952007-12-05 10:47:29 +01001515 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001516 else if (logsrv->addr.ss_family == AF_UNIX)
1517 plogfd = &logfdunix;
1518 else
1519 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001520
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001521 if (unlikely(*plogfd < 0)) {
1522 /* socket not successfully initialized yet */
1523 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1524 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1525 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001526
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001527 if (!once) {
1528 once = 1; /* note: no need for atomic ops here */
1529 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1530 nblogger, strerror(errno), errno);
1531 }
1532 return;
1533 } else {
1534 /* we don't want to receive anything on this socket */
1535 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1536 /* does nothing under Linux, maybe needed for others */
1537 shutdown(*plogfd, SHUT_RD);
1538 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1539 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001540 }
1541
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001542 switch (logsrv->format) {
1543 case LOG_FORMAT_RFC3164:
1544 hdr = logheader;
1545 hdr_ptr = update_log_hdr(time);
1546 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001547
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001548 case LOG_FORMAT_RFC5424:
1549 hdr = logheader_rfc5424;
1550 hdr_ptr = update_log_hdr_rfc5424(time);
1551 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1552 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001553
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001554 case LOG_FORMAT_SHORT:
1555 /* all fields are known, skip the header generation */
1556 hdr = logheader_short;
1557 hdr[0] = '<';
1558 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1559 hdr[2] = '>';
1560 hdr_ptr = hdr;
1561 hdr_max = 3;
1562 maxlen = logsrv->maxlen - hdr_max;
1563 max = MIN(size, maxlen) - 1;
1564 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001565
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001566 case LOG_FORMAT_RAW:
1567 /* all fields are known, skip the header generation */
1568 hdr_ptr = hdr = "";
1569 hdr_max = 0;
1570 maxlen = logsrv->maxlen;
1571 max = MIN(size, maxlen) - 1;
1572 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001573
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001574 default:
1575 return; /* must never happen */
1576 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001577
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001578 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001579
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001580 /* For each target, we may have a different facility.
1581 * We can also have a different log level for each message.
1582 * This induces variations in the message header length.
1583 * Since we don't want to recompute it each time, nor copy it every
1584 * time, we only change the facility in the pre-computed header,
1585 * and we change the pointer to the header accordingly.
1586 */
1587 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1588 hdr_ptr = hdr + 3; /* last digit of the log level */
1589 do {
1590 *hdr_ptr = '0' + fac_level % 10;
1591 fac_level /= 10;
1592 hdr_ptr--;
1593 } while (fac_level && hdr_ptr > hdr);
1594 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001595
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001596 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001597
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001598 /* time-based header */
1599 if (unlikely(hdr_size >= logsrv->maxlen)) {
1600 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1601 sd_max = 0;
1602 goto send;
1603 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001604
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001605 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001606
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001607 /* tag */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001608 tag_max = tag_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001609 if (unlikely(tag_max >= maxlen)) {
1610 tag_max = maxlen - 1;
1611 sd_max = 0;
1612 goto send;
1613 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001614
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001615 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001616
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001617 /* first pid separator */
1618 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1619 if (unlikely(pid_sep1_max >= maxlen)) {
1620 pid_sep1_max = maxlen - 1;
1621 sd_max = 0;
1622 goto send;
1623 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001624
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001625 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1626 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001627
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001628 /* pid */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001629 pid_max = pid_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001630 if (unlikely(pid_size >= maxlen)) {
1631 pid_size = maxlen - 1;
1632 sd_max = 0;
1633 goto send;
1634 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001635
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001636 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001637
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001638 /* second pid separator */
1639 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1640 if (unlikely(pid_sep2_max >= maxlen)) {
1641 pid_sep2_max = maxlen - 1;
1642 sd_max = 0;
1643 goto send;
1644 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001645
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001646 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1647 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001648
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001649 /* structured-data */
1650 if (sd_max >= maxlen) {
1651 sd_max = maxlen - 1;
1652 goto send;
1653 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001654
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001655 max = MIN(size, maxlen - sd_max) - 1;
1656send:
1657 iovec[0].iov_base = hdr_ptr;
1658 iovec[0].iov_len = hdr_max;
1659 iovec[1].iov_base = tag_str;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001660 iovec[1].iov_len = tag_max;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001661 iovec[2].iov_base = pid_sep1;
1662 iovec[2].iov_len = pid_sep1_max;
1663 iovec[3].iov_base = pid_str;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001664 iovec[3].iov_len = pid_max;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001665 iovec[4].iov_base = pid_sep2;
1666 iovec[4].iov_len = pid_sep2_max;
1667 iovec[5].iov_base = sd;
1668 iovec[5].iov_len = sd_max;
1669 iovec[6].iov_base = dataptr;
1670 iovec[6].iov_len = max;
1671 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1672 iovec[7].iov_len = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001673
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001674 if (logsrv->addr.ss_family == AF_UNSPEC) {
1675 /* the target is a direct file descriptor */
1676 sent = writev(*plogfd, iovec, 8);
1677 }
1678 else {
1679 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1680 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001681
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001682 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1683 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001684
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001685 if (sent < 0) {
1686 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001687
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001688 if (errno == EAGAIN)
1689 _HA_ATOMIC_ADD(&dropped_logs, 1);
1690 else if (!once) {
1691 once = 1; /* note: no need for atomic ops here */
1692 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1693 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001694 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001695 }
1696}
Dragan Dosen59cee972015-09-19 22:09:02 +02001697
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001698/*
1699 * This function sends a syslog message.
1700 * It doesn't care about errors nor does it report them.
1701 * The arguments <sd> and <sd_size> are used for the structured-data part
1702 * in RFC5424 formatted syslog messages.
1703 */
1704void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
1705{
1706 struct list *logsrvs = NULL;
1707 struct logsrv *logsrv;
1708 int nblogger;
1709 static THREAD_LOCAL int curr_pid;
1710 static THREAD_LOCAL char pidstr[100];
1711 static THREAD_LOCAL struct buffer pid;
1712 struct buffer *tag = &global.log_tag;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001713
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001714 if (p == NULL) {
1715 if (!LIST_ISEMPTY(&global.logsrvs)) {
1716 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001717 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001718 } else {
1719 if (!LIST_ISEMPTY(&p->logsrvs)) {
1720 logsrvs = &p->logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001721 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001722 if (p->log_tag.area) {
1723 tag = &p->log_tag;
1724 }
1725 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001726
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001727 if (!logsrvs)
1728 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001729
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001730 if (unlikely(curr_pid != getpid())) {
1731 curr_pid = getpid();
1732 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1733 chunk_initstr(&pid, pidstr);
1734 }
1735
1736 /* Send log messages to syslog server. */
1737 nblogger = 0;
1738 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001739 static THREAD_LOCAL int in_range = 1;
1740
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001741 /* we can filter the level of the messages that are sent to each logger */
1742 if (level > logsrv->level)
1743 continue;
1744
Frédéric Lécailled803e472019-04-25 07:42:09 +02001745 if (logsrv->lb.smp_rgs) {
1746 struct smp_log_range *curr_rg;
1747
1748 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1749 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1750 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1751 if (in_range) {
1752 /* Let's consume this range. */
1753 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1754 if (!curr_rg->curr_idx) {
1755 /* If consumed, let's select the next range. */
1756 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1757 }
1758 }
1759 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1760 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1761 }
1762 if (in_range)
1763 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1764 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001765 }
1766}
1767
William Lallemandbddd4fd2012-02-27 11:23:10 +01001768extern fd_set hdr_encode_map[];
1769extern fd_set url_encode_map[];
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001770extern fd_set http_encode_map[];
William Lallemandbddd4fd2012-02-27 11:23:10 +01001771
Willy Tarreaubaaee002006-06-26 02:48:02 +02001772
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001773const 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 +01001774const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1775 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1776 Set-cookie Updated, unknown, unknown */
1777
William Lallemand1d705562012-03-12 12:46:41 +01001778/*
1779 * try to write a character if there is enough space, or goto out
1780 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001781#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001782 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001783 *(tmplog++) = (x); \
1784 } else { \
1785 goto out; \
1786 } \
1787 } while(0)
1788
Dragan Dosen835b9212016-02-12 13:23:03 +01001789
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001790/* Initializes some log data at boot */
1791static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001792{
1793 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001794 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001795
1796 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1797 * inside PARAM-VALUE should be escaped with '\' as prefix.
1798 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1799 * details.
1800 */
1801 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1802
1803 tmp = "\"\\]";
1804 while (*tmp) {
1805 FD_SET(*tmp, rfc5424_escape_map);
1806 tmp++;
1807 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001808
1809 /* initialize the log header encoding map : '{|}"#' should be encoded with
1810 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1811 * URL encoding only requires '"', '#' to be encoded as well as non-
1812 * printable characters above.
1813 */
1814 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1815 memset(url_encode_map, 0, sizeof(url_encode_map));
1816 for (i = 0; i < 32; i++) {
1817 FD_SET(i, hdr_encode_map);
1818 FD_SET(i, url_encode_map);
1819 }
1820 for (i = 127; i < 256; i++) {
1821 FD_SET(i, hdr_encode_map);
1822 FD_SET(i, url_encode_map);
1823 }
1824
1825 tmp = "\"#{|}";
1826 while (*tmp) {
1827 FD_SET(*tmp, hdr_encode_map);
1828 tmp++;
1829 }
1830
1831 tmp = "\"#";
1832 while (*tmp) {
1833 FD_SET(*tmp, url_encode_map);
1834 tmp++;
1835 }
1836
1837 /* initialize the http header encoding map. The draft httpbis define the
1838 * header content as:
1839 *
1840 * HTTP-message = start-line
1841 * *( header-field CRLF )
1842 * CRLF
1843 * [ message-body ]
1844 * header-field = field-name ":" OWS field-value OWS
1845 * field-value = *( field-content / obs-fold )
1846 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1847 * obs-fold = CRLF 1*( SP / HTAB )
1848 * field-vchar = VCHAR / obs-text
1849 * VCHAR = %x21-7E
1850 * obs-text = %x80-FF
1851 *
1852 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1853 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001854 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001855 */
1856 memset(http_encode_map, 0, sizeof(http_encode_map));
1857 for (i = 0x00; i <= 0x08; i++)
1858 FD_SET(i, http_encode_map);
1859 for (i = 0x0a; i <= 0x1f; i++)
1860 FD_SET(i, http_encode_map);
1861 FD_SET(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001862}
William Lallemand1d705562012-03-12 12:46:41 +01001863
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001864INITCALL0(STG_PREPARE, init_log);
1865
Christopher Fauletcd7879a2017-10-27 13:53:47 +02001866static int init_log_buffers_per_thread()
1867{
1868 return init_log_buffers();
1869}
1870
1871static void deinit_log_buffers_per_thread()
1872{
1873 deinit_log_buffers();
1874}
1875
Christopher Faulet0132d062017-07-26 15:33:35 +02001876/* Initialize log buffers used for syslog messages */
1877int init_log_buffers()
1878{
1879 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001880 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001881 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001882 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001883 logline = my_realloc2(logline, global.max_syslog_len + 1);
1884 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1885 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1886 return 0;
1887 return 1;
1888}
1889
1890/* Deinitialize log buffers used for syslog messages */
1891void deinit_log_buffers()
1892{
Olivier Houchard7c497112019-03-07 14:19:24 +01001893 void *tmp_startup_logs;
1894
Christopher Faulet0132d062017-07-26 15:33:35 +02001895 free(logheader);
1896 free(logheader_rfc5424);
1897 free(logline);
1898 free(logline_rfc5424);
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001899 tmp_startup_logs = _HA_ATOMIC_XCHG(&startup_logs, NULL);
Olivier Houchard7c497112019-03-07 14:19:24 +01001900 free(tmp_startup_logs);
1901
Christopher Faulet0132d062017-07-26 15:33:35 +02001902 logheader = NULL;
1903 logheader_rfc5424 = NULL;
1904 logline = NULL;
1905 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001906 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001907}
1908
Willy Tarreaudf974472012-12-28 02:44:01 +01001909/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1910 * <maxsize> characters. Returns the size of the output string in characters,
1911 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001912 * is not zero. It requires a valid session and optionally a stream. If the
1913 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001914 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001915int 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 +02001916{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001917 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001918 struct proxy *be;
1919 struct http_txn *txn;
1920 const struct strm_logs *logs;
1921 const struct connection *be_conn;
1922 unsigned int s_flags;
1923 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001924 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001925 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001926 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001927 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001928 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001929 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001930 int t_request;
1931 int hdr;
1932 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001933 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001934 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001935 char *ret;
1936 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001937 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001938 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001939 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001940
William Lallemandbddd4fd2012-02-27 11:23:10 +01001941 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001942
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001943 if (likely(s)) {
1944 be = s->be;
1945 txn = s->txn;
1946 be_conn = cs_conn(objt_cs(s->si[1].end));
1947 s_flags = s->flags;
1948 uniq_id = s->uniq_id;
1949 logs = &s->logs;
1950 } else {
1951 /* we have no stream so we first need to initialize a few
1952 * things that are needed later. We do increment the request
1953 * ID so that it's uniquely assigned to this request just as
1954 * if the request had reached the point of being processed.
1955 * A request error is reported as it's the only element we have
1956 * here and which justifies emitting such a log.
1957 */
1958 be = fe;
1959 txn = NULL;
1960 be_conn = NULL;
1961 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001962 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001963
1964 /* prepare a valid log structure */
1965 tmp_strm_log.tv_accept = sess->tv_accept;
1966 tmp_strm_log.accept_date = sess->accept_date;
1967 tmp_strm_log.t_handshake = sess->t_handshake;
1968 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1969 tv_zero(&tmp_strm_log.tv_request);
1970 tmp_strm_log.t_queue = -1;
1971 tmp_strm_log.t_connect = -1;
1972 tmp_strm_log.t_data = -1;
1973 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1974 tmp_strm_log.bytes_in = 0;
1975 tmp_strm_log.bytes_out = 0;
1976 tmp_strm_log.prx_queue_pos = 0;
1977 tmp_strm_log.srv_queue_pos = 0;
1978
1979 logs = &tmp_strm_log;
1980 }
1981
William Lallemandbddd4fd2012-02-27 11:23:10 +01001982 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001983 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1984 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001985
William Lallemand1d705562012-03-12 12:46:41 +01001986 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001987
William Lallemandbddd4fd2012-02-27 11:23:10 +01001988 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001989 if (LIST_ISEMPTY(list_format))
1990 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001991
William Lallemand1d705562012-03-12 12:46:41 +01001992 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001993 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001994 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001995 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001996 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001997
Willy Tarreauc8368452012-12-21 00:09:23 +01001998 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01001999 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002000 if (!last_isspace) {
2001 LOGCHAR(' ');
2002 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002003 }
2004 break;
2005
William Lallemand1d705562012-03-12 12:46:41 +01002006 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002007 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002008 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002009 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002010 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002011 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002012 last_isspace = 0;
2013 break;
2014
Willy Tarreauc8368452012-12-21 00:09:23 +01002015 case LOG_FMT_EXPR: // sample expression, may be request or response
2016 key = NULL;
Olivier Houchardf90db442018-12-15 14:00:06 +01002017 if (tmp->options & LOG_OPT_REQ_CAP && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002018 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Olivier Houchardf90db442018-12-15 14:00:06 +01002019 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002020 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 +01002021 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002022 ret = lf_encode_chunk(tmplog, dst + maxsize,
2023 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002024 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002025 ret = lf_text_len(tmplog,
2026 key ? key->data.u.str.area : NULL,
2027 key ? key->data.u.str.data : 0,
2028 dst + maxsize - tmplog,
2029 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002030 if (ret == 0)
2031 goto out;
2032 tmplog = ret;
2033 last_isspace = 0;
2034 break;
2035
Willy Tarreau2beef582012-12-20 17:22:52 +01002036 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002037 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002038 if (conn)
2039 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.from, dst + maxsize - tmplog, tmp);
2040 else
2041 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002042 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002043 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002044 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002045 last_isspace = 0;
2046 break;
2047
Willy Tarreau2beef582012-12-20 17:22:52 +01002048 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002049 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002050 if (conn) {
2051 if (conn->addr.from.ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002052 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002053 } else {
2054 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.from,
2055 dst + maxsize - tmplog, tmp);
2056 }
William Lallemand5f232402012-04-05 18:02:55 +02002057 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002058 else
2059 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2060
William Lallemand5f232402012-04-05 18:02:55 +02002061 if (ret == NULL)
2062 goto out;
2063 tmplog = ret;
2064 last_isspace = 0;
2065 break;
2066
Willy Tarreau2beef582012-12-20 17:22:52 +01002067 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002068 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002069 if (conn) {
2070 conn_get_to_addr(conn);
2071 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
2072 }
2073 else
2074 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2075
William Lallemand1d705562012-03-12 12:46:41 +01002076 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002077 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002078 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002079 last_isspace = 0;
2080 break;
2081
Willy Tarreau2beef582012-12-20 17:22:52 +01002082 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002083 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002084 if (conn) {
2085 conn_get_to_addr(conn);
2086 if (conn->addr.to.ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002087 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002088 else
2089 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002090 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002091 else
2092 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2093
William Lallemand5f232402012-04-05 18:02:55 +02002094 if (ret == NULL)
2095 goto out;
2096 tmplog = ret;
2097 last_isspace = 0;
2098 break;
2099
Willy Tarreau2beef582012-12-20 17:22:52 +01002100 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002101 if (be_conn)
2102 ret = lf_ip(tmplog, (const struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002103 else
2104 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2105
William Lallemand1d705562012-03-12 12:46:41 +01002106 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002107 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002108 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002109 last_isspace = 0;
2110 break;
2111
Willy Tarreau2beef582012-12-20 17:22:52 +01002112 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002113 if (be_conn)
2114 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002115 else
2116 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2117
William Lallemand5f232402012-04-05 18:02:55 +02002118 if (ret == NULL)
2119 goto out;
2120 tmplog = ret;
2121 last_isspace = 0;
2122 break;
2123
Willy Tarreau2beef582012-12-20 17:22:52 +01002124 case LOG_FMT_SERVERIP: // %si
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002125 if (be_conn)
2126 ret = lf_ip(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002127 else
2128 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2129
William Lallemand5f232402012-04-05 18:02:55 +02002130 if (ret == NULL)
2131 goto out;
2132 tmplog = ret;
2133 last_isspace = 0;
2134 break;
2135
Willy Tarreau2beef582012-12-20 17:22:52 +01002136 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002137 if (be_conn)
2138 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002139 else
2140 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2141
William Lallemand1d705562012-03-12 12:46:41 +01002142 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002143 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002144 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002145 last_isspace = 0;
2146 break;
2147
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002148 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002149 get_localtime(logs->accept_date.tv_sec, &tm);
2150 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002151 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002152 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002153 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002154 last_isspace = 0;
2155 break;
2156
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002157 case LOG_FMT_tr: // %tr = start of request date
2158 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002159 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 +02002160 get_localtime(tv.tv_sec, &tm);
2161 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2162 if (ret == NULL)
2163 goto out;
2164 tmplog = ret;
2165 last_isspace = 0;
2166 break;
2167
2168 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002169 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002170 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002171 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002172 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002173 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002174 last_isspace = 0;
2175 break;
2176
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002177 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002178 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 +02002179 get_gmtime(tv.tv_sec, &tm);
2180 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2181 if (ret == NULL)
2182 goto out;
2183 tmplog = ret;
2184 last_isspace = 0;
2185 break;
2186
2187 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002188 get_localtime(logs->accept_date.tv_sec, &tm);
2189 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002190 if (ret == NULL)
2191 goto out;
2192 tmplog = ret;
2193 last_isspace = 0;
2194 break;
2195
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002196 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002197 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 +02002198 get_localtime(tv.tv_sec, &tm);
2199 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2200 if (ret == NULL)
2201 goto out;
2202 tmplog = ret;
2203 last_isspace = 0;
2204 break;
2205
William Lallemand5f232402012-04-05 18:02:55 +02002206 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002207 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002208 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002209 if (iret < 0 || iret > dst + maxsize - tmplog)
2210 goto out;
2211 last_isspace = 0;
2212 tmplog += iret;
2213 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002214 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002215 if (ret == NULL)
2216 goto out;
2217 tmplog = ret;
2218 last_isspace = 0;
2219 }
2220 break;
2221
William Lallemand1d705562012-03-12 12:46:41 +01002222 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002223 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002224 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002225 if (iret < 0 || iret > dst + maxsize - tmplog)
2226 goto out;
2227 last_isspace = 0;
2228 tmplog += iret;
2229 } else {
2230 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002231 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002232 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002233 tmplog, 4);
2234 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002235 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002236 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002237 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002238 }
2239 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002240
William Lallemand1d705562012-03-12 12:46:41 +01002241 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002242 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002243 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002244 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002245 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002246 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002247 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002248 break;
2249
Willy Tarreau773d65f2012-10-12 14:56:11 +02002250 case LOG_FMT_FRONTEND_XPRT: // %ft
2251 src = fe->id;
2252 if (tmp->options & LOG_OPT_QUOTE)
2253 LOGCHAR('"');
2254 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2255 if (iret == 0)
2256 goto out;
2257 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002258 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002259 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002260 if (tmp->options & LOG_OPT_QUOTE)
2261 LOGCHAR('"');
2262 last_isspace = 0;
2263 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002264#ifdef USE_OPENSSL
2265 case LOG_FMT_SSL_CIPHER: // %sslc
2266 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002267 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002268 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002269 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002270 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002271 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2272 if (ret == NULL)
2273 goto out;
2274 tmplog = ret;
2275 last_isspace = 0;
2276 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002277
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002278 case LOG_FMT_SSL_VERSION: // %sslv
2279 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002280 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002281 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002282 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002283 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002284 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2285 if (ret == NULL)
2286 goto out;
2287 tmplog = ret;
2288 last_isspace = 0;
2289 break;
2290#endif
William Lallemand1d705562012-03-12 12:46:41 +01002291 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002292 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002293 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002294 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002295 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002296 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002297 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002298 break;
2299
William Lallemand1d705562012-03-12 12:46:41 +01002300 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002301 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002302 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002303 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002304 break;
2305 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002306 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002307 break;
2308 default:
2309 src = "<NOSRV>";
2310 break;
2311 }
William Lallemand5f232402012-04-05 18:02:55 +02002312 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002313 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002314 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002315 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002316 last_isspace = 0;
2317 break;
2318
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002319 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002320 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002321 if (ret == NULL)
2322 goto out;
2323 tmplog = ret;
2324 last_isspace = 0;
2325 break;
2326
2327 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002328 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002329 if (ret == NULL)
2330 goto out;
2331 tmplog = ret;
2332 last_isspace = 0;
2333 break;
2334
2335 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002336 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002337 tmplog, dst + maxsize - tmplog);
2338 if (ret == NULL)
2339 goto out;
2340 tmplog = ret;
2341 last_isspace = 0;
2342 break;
2343
2344 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002345 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002346 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002347 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002348 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002349 last_isspace = 0;
2350 break;
2351
William Lallemand1d705562012-03-12 12:46:41 +01002352 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002353 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002354 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002355 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002356 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002357 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002358 last_isspace = 0;
2359 break;
2360
William Lallemand1d705562012-03-12 12:46:41 +01002361 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002362 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002363 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002364 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002365 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002366 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002367 last_isspace = 0;
2368 break;
2369
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002370 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002371 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002372 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002373 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002374 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002375 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002376 last_isspace = 0;
2377 break;
2378
Willy Tarreau27b639d2016-05-17 17:55:27 +02002379 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002380 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002381 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002382 tmplog, dst + maxsize - tmplog);
2383 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002384 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002385 tmplog, dst + maxsize - tmplog);
2386 if (ret == NULL)
2387 goto out;
2388 tmplog = ret;
2389 last_isspace = 0;
2390 break;
2391
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002392 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2393 if (!(fe->to_log & LW_BYTES))
2394 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002395 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 +02002396 tmplog, dst + maxsize - tmplog);
2397 if (ret == NULL)
2398 goto out;
2399 tmplog = ret;
2400 last_isspace = 0;
2401 break;
2402
2403 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002404 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002405 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002406 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002407 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002408 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002409 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002410 last_isspace = 0;
2411 break;
2412
Willy Tarreau2beef582012-12-20 17:22:52 +01002413 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002414 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002415 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002416 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002417 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002418 last_isspace = 0;
2419 break;
2420
William Lallemand1d705562012-03-12 12:46:41 +01002421 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002422 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002423 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002424 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002425 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002426 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002427 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002428 last_isspace = 0;
2429 break;
2430
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002431 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002432 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002433 if (ret == NULL)
2434 goto out;
2435 tmplog = ret;
2436 last_isspace = 0;
2437 break;
2438
Willy Tarreau2beef582012-12-20 17:22:52 +01002439 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002440 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002441 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002442 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002443 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002444 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002445 last_isspace = 0;
2446 break;
2447
Willy Tarreau2beef582012-12-20 17:22:52 +01002448 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002449 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002450 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002451 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002452 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002453 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002454 last_isspace = 0;
2455 break;
2456
William Lallemand1d705562012-03-12 12:46:41 +01002457 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002458 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2459 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002460 *tmplog = '\0';
2461 last_isspace = 0;
2462 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002463
William Lallemand1d705562012-03-12 12:46:41 +01002464 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002465 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2466 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002467 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2468 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 +01002469 last_isspace = 0;
2470 break;
2471
William Lallemand1d705562012-03-12 12:46:41 +01002472 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002473 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002474 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002475 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002476 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002477 last_isspace = 0;
2478 break;
2479
William Lallemand1d705562012-03-12 12:46:41 +01002480 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002481 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002482 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002483 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002484 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002485 last_isspace = 0;
2486 break;
2487
William Lallemand1d705562012-03-12 12:46:41 +01002488 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002489 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002490 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002491 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002492 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002493 last_isspace = 0;
2494 break;
2495
William Lallemand1d705562012-03-12 12:46:41 +01002496 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002497 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002498 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002499 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002500 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002501 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002502 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002503 last_isspace = 0;
2504 break;
2505
William Lallemand1d705562012-03-12 12:46:41 +01002506 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002507 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002508 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002509 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002510 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002511 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002512 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002513 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002514 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002515 last_isspace = 0;
2516 break;
2517
William Lallemand1d705562012-03-12 12:46:41 +01002518 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002519 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002520 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002521 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002522 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002523 last_isspace = 0;
2524 break;
2525
William Lallemand1d705562012-03-12 12:46:41 +01002526 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002527 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002528 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002529 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002530 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002531 last_isspace = 0;
2532 break;
2533
William Lallemand1d705562012-03-12 12:46:41 +01002534 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002535 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002536 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002537 if (tmp->options & LOG_OPT_QUOTE)
2538 LOGCHAR('"');
2539 LOGCHAR('{');
2540 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2541 if (hdr)
2542 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002543 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002544 ret = lf_encode_string(tmplog, dst + maxsize,
2545 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002546 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002547 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002548 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002549 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002550 }
2551 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002552 if (tmp->options & LOG_OPT_QUOTE)
2553 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002554 last_isspace = 0;
2555 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002556 break;
2557
William Lallemand1d705562012-03-12 12:46:41 +01002558 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002559 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002560 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002561 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2562 if (hdr > 0)
2563 LOGCHAR(' ');
2564 if (tmp->options & LOG_OPT_QUOTE)
2565 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002566 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002567 ret = lf_encode_string(tmplog, dst + maxsize,
2568 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002569 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002570 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002571 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002572 } else if (!(tmp->options & LOG_OPT_QUOTE))
2573 LOGCHAR('-');
2574 if (tmp->options & LOG_OPT_QUOTE)
2575 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002576 last_isspace = 0;
2577 }
2578 }
2579 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002580
William Lallemand1d705562012-03-12 12:46:41 +01002581
2582 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002583 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002584 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002585 if (tmp->options & LOG_OPT_QUOTE)
2586 LOGCHAR('"');
2587 LOGCHAR('{');
2588 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2589 if (hdr)
2590 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002591 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002592 ret = lf_encode_string(tmplog, dst + maxsize,
2593 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002594 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002595 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002596 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002597 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002598 }
2599 LOGCHAR('}');
2600 last_isspace = 0;
2601 if (tmp->options & LOG_OPT_QUOTE)
2602 LOGCHAR('"');
2603 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002604 break;
2605
William Lallemand1d705562012-03-12 12:46:41 +01002606 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002607 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002608 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002609 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2610 if (hdr > 0)
2611 LOGCHAR(' ');
2612 if (tmp->options & LOG_OPT_QUOTE)
2613 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002614 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002615 ret = lf_encode_string(tmplog, dst + maxsize,
2616 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002617 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002618 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002619 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002620 } else if (!(tmp->options & LOG_OPT_QUOTE))
2621 LOGCHAR('-');
2622 if (tmp->options & LOG_OPT_QUOTE)
2623 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002624 last_isspace = 0;
2625 }
2626 }
2627 break;
2628
William Lallemand1d705562012-03-12 12:46:41 +01002629 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002630 /* Request */
2631 if (tmp->options & LOG_OPT_QUOTE)
2632 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002633 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002634 ret = lf_encode_string(tmplog, dst + maxsize,
2635 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002636 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002637 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002638 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002639 if (tmp->options & LOG_OPT_QUOTE)
2640 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002641 last_isspace = 0;
2642 break;
William Lallemand5f232402012-04-05 18:02:55 +02002643
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002644 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002645 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002646
Willy Tarreaub7636d12015-06-17 19:58:02 +02002647 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002648 LOGCHAR('"');
2649
2650 end = uri + strlen(uri);
2651 // look for the first whitespace character
2652 while (uri < end && !HTTP_IS_SPHT(*uri))
2653 uri++;
2654
2655 // keep advancing past multiple spaces
2656 while (uri < end && HTTP_IS_SPHT(*uri)) {
2657 uri++; nspaces++;
2658 }
2659
2660 // look for first space or question mark after url
2661 spc = uri;
2662 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2663 spc++;
2664
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002665 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002666 chunk.area = "<BADREQ>";
2667 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002668 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002669 chunk.area = uri;
2670 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002671 }
2672
Dragan Dosen835b9212016-02-12 13:23:03 +01002673 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002674 if (ret == NULL || *ret != '\0')
2675 goto out;
2676
2677 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002678 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002679 LOGCHAR('"');
2680
2681 last_isspace = 0;
2682 break;
2683
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002684 case LOG_FMT_HTTP_QUERY: // %HQ
2685 if (tmp->options & LOG_OPT_QUOTE)
2686 LOGCHAR('"');
2687
Willy Tarreau57bc8912016-04-25 17:09:40 +02002688 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002689 chunk.area = "<BADREQ>";
2690 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002691 } else {
2692 uri = txn->uri;
2693 end = uri + strlen(uri);
2694 // look for the first question mark
2695 while (uri < end && *uri != '?')
2696 uri++;
2697
2698 qmark = uri;
2699 // look for first space or question mark after url
2700 while (uri < end && !HTTP_IS_SPHT(*uri))
2701 uri++;
2702
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002703 chunk.area = qmark;
2704 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002705 }
2706
Dragan Dosen835b9212016-02-12 13:23:03 +01002707 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002708 if (ret == NULL || *ret != '\0')
2709 goto out;
2710
2711 tmplog = ret;
2712 if (tmp->options & LOG_OPT_QUOTE)
2713 LOGCHAR('"');
2714
2715 last_isspace = 0;
2716 break;
2717
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002718 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002719 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002720
Willy Tarreaub7636d12015-06-17 19:58:02 +02002721 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002722 LOGCHAR('"');
2723
2724 end = uri + strlen(uri);
2725 // look for the first whitespace character
2726 while (uri < end && !HTTP_IS_SPHT(*uri))
2727 uri++;
2728
2729 // keep advancing past multiple spaces
2730 while (uri < end && HTTP_IS_SPHT(*uri)) {
2731 uri++; nspaces++;
2732 }
2733
2734 // look for first space after url
2735 spc = uri;
2736 while (spc < end && !HTTP_IS_SPHT(*spc))
2737 spc++;
2738
Willy Tarreau57bc8912016-04-25 17:09:40 +02002739 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002740 chunk.area = "<BADREQ>";
2741 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002742 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002743 chunk.area = uri;
2744 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002745 }
2746
Dragan Dosen835b9212016-02-12 13:23:03 +01002747 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002748 if (ret == NULL || *ret != '\0')
2749 goto out;
2750
2751 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002752 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002753 LOGCHAR('"');
2754
2755 last_isspace = 0;
2756 break;
2757
2758 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002759 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002760 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002761 LOGCHAR('"');
2762
2763 end = uri + strlen(uri);
2764 // look for the first whitespace character
2765 spc = uri;
2766 while (spc < end && !HTTP_IS_SPHT(*spc))
2767 spc++;
2768
2769 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002770 chunk.area = "<BADREQ>";
2771 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002772 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002773 chunk.area = uri;
2774 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002775 }
2776
Dragan Dosen835b9212016-02-12 13:23:03 +01002777 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002778 if (ret == NULL || *ret != '\0')
2779 goto out;
2780
2781 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002782 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002783 LOGCHAR('"');
2784
2785 last_isspace = 0;
2786 break;
2787
2788 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002789 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002790 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002791 LOGCHAR('"');
2792
2793 end = uri + strlen(uri);
2794 // look for the first whitespace character
2795 while (uri < end && !HTTP_IS_SPHT(*uri))
2796 uri++;
2797
2798 // keep advancing past multiple spaces
2799 while (uri < end && HTTP_IS_SPHT(*uri)) {
2800 uri++; nspaces++;
2801 }
2802
2803 // look for the next whitespace character
2804 while (uri < end && !HTTP_IS_SPHT(*uri))
2805 uri++;
2806
2807 // keep advancing past multiple spaces
2808 while (uri < end && HTTP_IS_SPHT(*uri))
2809 uri++;
2810
Willy Tarreau57bc8912016-04-25 17:09:40 +02002811 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002812 chunk.area = "<BADREQ>";
2813 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002814 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002815 chunk.area = "HTTP/0.9";
2816 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002817 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002818 chunk.area = uri;
2819 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002820 }
2821
Dragan Dosen835b9212016-02-12 13:23:03 +01002822 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002823 if (ret == NULL || *ret != '\0')
2824 goto out;
2825
2826 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002827 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002828 LOGCHAR('"');
2829
2830 last_isspace = 0;
2831 break;
2832
William Lallemand5f232402012-04-05 18:02:55 +02002833 case LOG_FMT_COUNTER: // %rt
2834 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002835 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002836 if (iret < 0 || iret > dst + maxsize - tmplog)
2837 goto out;
2838 last_isspace = 0;
2839 tmplog += iret;
2840 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002841 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002842 if (ret == NULL)
2843 goto out;
2844 tmplog = ret;
2845 last_isspace = 0;
2846 }
2847 break;
2848
Willy Tarreau7346acb2014-08-28 15:03:15 +02002849 case LOG_FMT_LOGCNT: // %lc
2850 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002851 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002852 if (iret < 0 || iret > dst + maxsize - tmplog)
2853 goto out;
2854 last_isspace = 0;
2855 tmplog += iret;
2856 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002857 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002858 if (ret == NULL)
2859 goto out;
2860 tmplog = ret;
2861 last_isspace = 0;
2862 }
2863 break;
2864
William Lallemand5f232402012-04-05 18:02:55 +02002865 case LOG_FMT_HOSTNAME: // %H
2866 src = hostname;
2867 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2868 if (ret == NULL)
2869 goto out;
2870 tmplog = ret;
2871 last_isspace = 0;
2872 break;
2873
2874 case LOG_FMT_PID: // %pid
2875 if (tmp->options & LOG_OPT_HEXA) {
2876 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2877 if (iret < 0 || iret > dst + maxsize - tmplog)
2878 goto out;
2879 last_isspace = 0;
2880 tmplog += iret;
2881 } else {
2882 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2883 if (ret == NULL)
2884 goto out;
2885 tmplog = ret;
2886 last_isspace = 0;
2887 }
2888 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002889
2890 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002891 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002892 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002893 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002894 if (ret == NULL)
2895 goto out;
2896 tmplog = ret;
2897 last_isspace = 0;
2898 break;
2899
William Lallemandbddd4fd2012-02-27 11:23:10 +01002900 }
2901 }
2902
2903out:
William Lallemand1d705562012-03-12 12:46:41 +01002904 /* *tmplog is a unused character */
2905 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002906 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002907
Willy Tarreaubaaee002006-06-26 02:48:02 +02002908}
2909
William Lallemand1d705562012-03-12 12:46:41 +01002910/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002911 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002912 * Will not log if the frontend has no log defined.
2913 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002914void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002915{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002916 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002917 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002918 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002919
2920 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002921 err = (s->flags & SF_REDISP) ||
2922 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2923 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002924 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002925 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002926
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002927 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002928 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002929
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002930 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002931 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002932
Willy Tarreauabcd5142013-06-11 17:18:02 +02002933 if (s->logs.level) { /* loglevel was overridden */
2934 if (s->logs.level == -1) {
2935 s->logs.logwait = 0; /* logs disabled */
2936 return;
2937 }
2938 level = s->logs.level - 1;
2939 }
2940 else {
2941 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002942 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002943 level = LOG_ERR;
2944 }
William Lallemand1d705562012-03-12 12:46:41 +01002945
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002946 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002947 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002948 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002949 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002950 }
2951
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002952 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2953 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2954 &sess->fe->logformat_sd);
2955 }
2956
Dragan Dosen59cee972015-09-19 22:09:02 +02002957 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002958 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002959 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002960 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002961 s->logs.logwait = 0;
2962 }
2963}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002964
Willy Tarreau53839352018-09-05 19:51:10 +02002965/*
2966 * send a minimalist log for the session. Will not log if the frontend has no
2967 * log defined. It is assumed that this is only used to report anomalies that
2968 * cannot lead to the creation of a regular stream. Because of this the log
2969 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2970 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002971 * function to report unimportant events. It is safe to call this function with
2972 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002973 */
2974void sess_log(struct session *sess)
2975{
2976 int size, level;
2977 int sd_size = 0;
2978
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002979 if (!sess)
2980 return;
2981
Willy Tarreau53839352018-09-05 19:51:10 +02002982 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2983 return;
2984
2985 level = LOG_INFO;
2986 if (sess->fe->options2 & PR_O2_LOGERRORS)
2987 level = LOG_ERR;
2988
2989 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2990 sd_size = sess_build_logline(sess, NULL,
2991 logline_rfc5424, global.max_syslog_len,
2992 &sess->fe->logformat_sd);
2993 }
2994
2995 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2996 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002997 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Willy Tarreau53839352018-09-05 19:51:10 +02002998 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
2999 }
3000}
3001
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003002static int cli_io_handler_show_startup_logs(struct appctx *appctx)
3003{
3004 struct stream_interface *si = appctx->owner;
3005 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
3006
3007 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01003008 si_rx_room_blk(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003009 return 0;
3010 }
3011 return 1;
3012}
3013
3014/* register cli keywords */
3015static struct cli_kw_list cli_kws = {{ },{
3016 { { "show", "startup-logs", NULL },
3017 "show startup-logs : report logs emitted during HAProxy startup",
3018 NULL, cli_io_handler_show_startup_logs },
3019 {{},}
3020}};
3021
Willy Tarreau0108d902018-11-25 19:14:37 +01003022INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3023
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003024REGISTER_PER_THREAD_INIT(init_log_buffers_per_thread);
3025REGISTER_PER_THREAD_DEINIT(deinit_log_buffers_per_thread);
3026
Willy Tarreaubaaee002006-06-26 02:48:02 +02003027/*
3028 * Local variables:
3029 * c-indent-level: 8
3030 * c-basic-offset: 8
3031 * End:
3032 */