blob: 53352cbd165cbea4e9d0c9ede7f68ea965f2834d [file] [log] [blame]
Willy Tarreau75014fc2022-08-12 11:23:59 +02001/*
2 * post-mortem ring reader for haproxy
3 *
4 * Copyright (C) 2022 Willy Tarreau <w@1wt.eu>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27#include <sys/mman.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stdlib.h>
33
34#include <haproxy/api.h>
35#include <haproxy/buf.h>
36#include <haproxy/ring.h>
37
38int force = 0; // force access to a different layout
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020039int lfremap = 0; // remap LF in traces
Willy Tarreaue06ba902023-01-24 12:13:14 +010040int repair = 0; // repair file
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020041
Willy Tarreau75014fc2022-08-12 11:23:59 +020042
43/* display the message and exit with the code */
44__attribute__((noreturn)) void die(int code, const char *format, ...)
45{
46 va_list args;
47
48 if (format) {
49 va_start(args, format);
50 vfprintf(stderr, format, args);
51 va_end(args);
52 }
53 exit(code);
54}
55
56/* display the usage message and exit with the code */
57__attribute__((noreturn)) void usage(int code, const char *arg0)
58{
59 die(code,
60 "Usage: %s [options]* <file>\n"
61 "\n"
62 "options :\n"
63 " -f : force accessing a non-matching layout for 'ring struct'\n"
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020064 " -l : replace LF in contents with CR VT\n"
Willy Tarreaue06ba902023-01-24 12:13:14 +010065 " -r : \"repair\" corrupted file (actively search for message boundaries)\n"
Willy Tarreau75014fc2022-08-12 11:23:59 +020066 "\n"
67 "", arg0);
68}
69
70/* This function dumps all events from the ring whose pointer is in <p0> into
71 * the appctx's output buffer, and takes from <o0> the seek offset into the
72 * buffer's history (0 for oldest known event). It looks at <i0> for boolean
73 * options: bit0 means it must wait for new data or any key to be pressed. Bit1
74 * means it must seek directly to the end to wait for new contents. It returns
75 * 0 if the output buffer or events are missing is full and it needs to be
76 * called again, otherwise non-zero. It is meant to be used with
77 * cli_release_show_ring() to clean up.
78 */
79int dump_ring(struct ring *ring, size_t ofs, int flags)
80{
81 struct buffer buf;
82 uint64_t msg_len = 0;
83 size_t len, cnt;
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020084 const char *blk1 = NULL, *blk2 = NULL, *p;
85 size_t len1 = 0, len2 = 0, bl;
Willy Tarreau75014fc2022-08-12 11:23:59 +020086
87 /* Explanation: the storage area in the writing process starts after
88 * the end of the structure. Since the whole area is mmapped(), we know
89 * it starts at 0 mod 4096, hence the buf->area pointer's 12 LSB point
90 * to the relative offset of the storage area. As there will always be
91 * users using the wrong version of the tool with a dump, we need to
92 * run a few checks first. After that we'll create our own buffer
93 * descriptor matching that area.
94 */
95 if ((((long)ring->buf.area) & 4095) != sizeof(*ring)) {
96 if (!force) {
97 fprintf(stderr, "FATAL: header in file is %ld bytes long vs %ld expected!\n",
98 (((long)ring->buf.area) & 4095),
99 (long)sizeof(*ring));
100 exit(1);
101 }
102 else {
103 fprintf(stderr, "WARNING: header in file is %ld bytes long vs %ld expected!\n",
104 (((long)ring->buf.area) & 4095),
105 (long)sizeof(*ring));
106 }
107 /* maybe we could emit a warning at least ? */
108 }
109
110 /* Now make our own buffer pointing to that area */
111 buf = b_make(((void *)ring + (((long)ring->buf.area) & 4095)),
112 ring->buf.size, ring->buf.head, ring->buf.data);
113
114 /* explanation for the initialization below: it would be better to do
115 * this in the parsing function but this would occasionally result in
116 * dropped events because we'd take a reference on the oldest message
117 * and keep it while being scheduled. Thus instead let's take it the
118 * first time we enter here so that we have a chance to pass many
119 * existing messages before grabbing a reference to a location. This
120 * value cannot be produced after initialization.
121 */
122 if (unlikely(ofs == ~0)) {
123 ofs = 0;
124
125 /* going to the end means looking at tail-1 */
126 if (flags & RING_WF_SEEK_NEW)
127 ofs += b_data(&buf) - 1;
128
129 //HA_ATOMIC_INC(b_peek(&buf, ofs));
130 ofs += ring->ofs;
131 }
132
133 while (1) {
134 //HA_RWLOCK_RDLOCK(LOGSRV_LOCK, &ring->lock);
135
136 /* we were already there, adjust the offset to be relative to
137 * the buffer's head and remove us from the counter.
138 */
139 ofs -= ring->ofs;
140 if (ofs >= buf.size) {
141 fprintf(stderr, "FATAL error at %d\n", __LINE__);
142 return 1;
143 }
144 //HA_ATOMIC_DEC(b_peek(&buf, ofs));
145
146 /* in this loop, ofs always points to the counter byte that precedes
147 * the message so that we can take our reference there if we have to
148 * stop before the end.
149 */
150 while (ofs + 1 < b_data(&buf)) {
Willy Tarreaue06ba902023-01-24 12:13:14 +0100151 if (unlikely(repair && *b_peek(&buf, ofs))) {
152 /* in repair mode we consider that we could have landed
153 * in the middle of a message so we skip all bytes till
154 * the next zero.
155 */
156 ofs++;
157 continue;
158 }
Willy Tarreau75014fc2022-08-12 11:23:59 +0200159 cnt = 1;
160 len = b_peek_varint(&buf, ofs + cnt, &msg_len);
161 if (!len)
162 break;
163 cnt += len;
164
165 if (msg_len + ofs + cnt + 1 > buf.data) {
166 fprintf(stderr, "FATAL error at %d\n", __LINE__);
167 return 1;
168 }
169
170 len = b_getblk_nc(&buf, &blk1, &len1, &blk2, &len2, ofs + cnt, msg_len);
Willy Tarreaucc51c9a2022-08-12 12:09:41 +0200171 if (!lfremap) {
172 if (len > 0 && len1)
173 fwrite(blk1, len1, 1, stdout);
174 if (len > 1 && len2)
175 fwrite(blk2, len2, 1, stdout);
176 } else {
177 while (len > 0) {
178 for (; len1; p++) {
179 p = memchr(blk1, '\n', len1);
180 if (!p || p > blk1) {
181 bl = p ? p - blk1 : len1;
182 fwrite(blk1, bl, 1, stdout);
183 blk1 += bl;
184 len1 -= bl;
185 }
186
187 if (p) {
188 putchar('\r');
189 putchar('\v');
190 blk1++;
191 len1--;
192 }
193 }
194 len--;
195 blk1 = blk2;
196 len1 = len2;
197 }
198 }
199
200 putchar('\n');
Willy Tarreau75014fc2022-08-12 11:23:59 +0200201
202 ofs += cnt + msg_len;
203 }
204
205 //HA_ATOMIC_INC(b_peek(&buf, ofs));
206 ofs += ring->ofs;
207 //HA_RWLOCK_RDUNLOCK(LOGSRV_LOCK, &ring->lock);
208
209 if (!(flags & RING_WF_WAIT_MODE))
210 break;
211
212 /* pause 10ms before checking for new stuff */
213 usleep(10000);
214 }
215 return 0;
216}
217
218int main(int argc, char **argv)
219{
220 struct ring *ring;
221 struct stat statbuf;
222 const char *arg0;
223 int fd;
224
225 arg0 = argv[0];
226 while (argc > 1 && argv[1][0] == '-') {
227 argc--; argv++;
228 if (strcmp(argv[0], "-f") == 0)
229 force = 1;
Willy Tarreaucc51c9a2022-08-12 12:09:41 +0200230 else if (strcmp(argv[0], "-l") == 0)
231 lfremap = 1;
Willy Tarreaue06ba902023-01-24 12:13:14 +0100232 else if (strcmp(argv[0], "-r") == 0)
233 repair = 1;
Willy Tarreau75014fc2022-08-12 11:23:59 +0200234 else if (strcmp(argv[0], "--") == 0)
235 break;
236 else
237 usage(1, arg0);
238 }
239
240 if (argc < 2)
241 usage(1, arg0);
242
243 fd = open(argv[1], O_RDONLY);
244 if (fd < 0) {
245 perror("open()");
246 return 1;
247 }
248
249 if (fstat(fd, &statbuf) < 0) {
250 perror("fstat()");
251 return 1;
252 }
253
254 ring = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
255 close(fd);
256
257 if (ring == MAP_FAILED) {
258 perror("mmap()");
259 return 1;
260 }
261
262 return dump_ring(ring, ~0, 0);
263}
264
265
266/*
267 * Local variables:
268 * c-indent-level: 8
269 * c-basic-offset: 8
270 * End:
271 */