summaryrefslogtreecommitdiffstats
path: root/dev/haring
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dev/haring/README4
-rw-r--r--dev/haring/haring.c271
2 files changed, 275 insertions, 0 deletions
diff --git a/dev/haring/README b/dev/haring/README
new file mode 100644
index 0000000..69d08a8
--- /dev/null
+++ b/dev/haring/README
@@ -0,0 +1,4 @@
+This needs to be built from the top makefile, for example :
+
+ make dev/haring/haring
+
diff --git a/dev/haring/haring.c b/dev/haring/haring.c
new file mode 100644
index 0000000..53352cb
--- /dev/null
+++ b/dev/haring/haring.c
@@ -0,0 +1,271 @@
+/*
+ * post-mortem ring reader for haproxy
+ *
+ * Copyright (C) 2022 Willy Tarreau <w@1wt.eu>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <haproxy/api.h>
+#include <haproxy/buf.h>
+#include <haproxy/ring.h>
+
+int force = 0; // force access to a different layout
+int lfremap = 0; // remap LF in traces
+int repair = 0; // repair file
+
+
+/* display the message and exit with the code */
+__attribute__((noreturn)) void die(int code, const char *format, ...)
+{
+ va_list args;
+
+ if (format) {
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ }
+ exit(code);
+}
+
+/* display the usage message and exit with the code */
+__attribute__((noreturn)) void usage(int code, const char *arg0)
+{
+ die(code,
+ "Usage: %s [options]* <file>\n"
+ "\n"
+ "options :\n"
+ " -f : force accessing a non-matching layout for 'ring struct'\n"
+ " -l : replace LF in contents with CR VT\n"
+ " -r : \"repair\" corrupted file (actively search for message boundaries)\n"
+ "\n"
+ "", arg0);
+}
+
+/* This function dumps all events from the ring whose pointer is in <p0> into
+ * the appctx's output buffer, and takes from <o0> the seek offset into the
+ * buffer's history (0 for oldest known event). It looks at <i0> for boolean
+ * options: bit0 means it must wait for new data or any key to be pressed. Bit1
+ * means it must seek directly to the end to wait for new contents. It returns
+ * 0 if the output buffer or events are missing is full and it needs to be
+ * called again, otherwise non-zero. It is meant to be used with
+ * cli_release_show_ring() to clean up.
+ */
+int dump_ring(struct ring *ring, size_t ofs, int flags)
+{
+ struct buffer buf;
+ uint64_t msg_len = 0;
+ size_t len, cnt;
+ const char *blk1 = NULL, *blk2 = NULL, *p;
+ size_t len1 = 0, len2 = 0, bl;
+
+ /* Explanation: the storage area in the writing process starts after
+ * the end of the structure. Since the whole area is mmapped(), we know
+ * it starts at 0 mod 4096, hence the buf->area pointer's 12 LSB point
+ * to the relative offset of the storage area. As there will always be
+ * users using the wrong version of the tool with a dump, we need to
+ * run a few checks first. After that we'll create our own buffer
+ * descriptor matching that area.
+ */
+ if ((((long)ring->buf.area) & 4095) != sizeof(*ring)) {
+ if (!force) {
+ fprintf(stderr, "FATAL: header in file is %ld bytes long vs %ld expected!\n",
+ (((long)ring->buf.area) & 4095),
+ (long)sizeof(*ring));
+ exit(1);
+ }
+ else {
+ fprintf(stderr, "WARNING: header in file is %ld bytes long vs %ld expected!\n",
+ (((long)ring->buf.area) & 4095),
+ (long)sizeof(*ring));
+ }
+ /* maybe we could emit a warning at least ? */
+ }
+
+ /* Now make our own buffer pointing to that area */
+ buf = b_make(((void *)ring + (((long)ring->buf.area) & 4095)),
+ ring->buf.size, ring->buf.head, ring->buf.data);
+
+ /* explanation for the initialization below: it would be better to do
+ * this in the parsing function but this would occasionally result in
+ * dropped events because we'd take a reference on the oldest message
+ * and keep it while being scheduled. Thus instead let's take it the
+ * first time we enter here so that we have a chance to pass many
+ * existing messages before grabbing a reference to a location. This
+ * value cannot be produced after initialization.
+ */
+ if (unlikely(ofs == ~0)) {
+ ofs = 0;
+
+ /* going to the end means looking at tail-1 */
+ if (flags & RING_WF_SEEK_NEW)
+ ofs += b_data(&buf) - 1;
+
+ //HA_ATOMIC_INC(b_peek(&buf, ofs));
+ ofs += ring->ofs;
+ }
+
+ while (1) {
+ //HA_RWLOCK_RDLOCK(LOGSRV_LOCK, &ring->lock);
+
+ /* we were already there, adjust the offset to be relative to
+ * the buffer's head and remove us from the counter.
+ */
+ ofs -= ring->ofs;
+ if (ofs >= buf.size) {
+ fprintf(stderr, "FATAL error at %d\n", __LINE__);
+ return 1;
+ }
+ //HA_ATOMIC_DEC(b_peek(&buf, ofs));
+
+ /* in this loop, ofs always points to the counter byte that precedes
+ * the message so that we can take our reference there if we have to
+ * stop before the end.
+ */
+ while (ofs + 1 < b_data(&buf)) {
+ if (unlikely(repair && *b_peek(&buf, ofs))) {
+ /* in repair mode we consider that we could have landed
+ * in the middle of a message so we skip all bytes till
+ * the next zero.
+ */
+ ofs++;
+ continue;
+ }
+ cnt = 1;
+ len = b_peek_varint(&buf, ofs + cnt, &msg_len);
+ if (!len)
+ break;
+ cnt += len;
+
+ if (msg_len + ofs + cnt + 1 > buf.data) {
+ fprintf(stderr, "FATAL error at %d\n", __LINE__);
+ return 1;
+ }
+
+ len = b_getblk_nc(&buf, &blk1, &len1, &blk2, &len2, ofs + cnt, msg_len);
+ if (!lfremap) {
+ if (len > 0 && len1)
+ fwrite(blk1, len1, 1, stdout);
+ if (len > 1 && len2)
+ fwrite(blk2, len2, 1, stdout);
+ } else {
+ while (len > 0) {
+ for (; len1; p++) {
+ p = memchr(blk1, '\n', len1);
+ if (!p || p > blk1) {
+ bl = p ? p - blk1 : len1;
+ fwrite(blk1, bl, 1, stdout);
+ blk1 += bl;
+ len1 -= bl;
+ }
+
+ if (p) {
+ putchar('\r');
+ putchar('\v');
+ blk1++;
+ len1--;
+ }
+ }
+ len--;
+ blk1 = blk2;
+ len1 = len2;
+ }
+ }
+
+ putchar('\n');
+
+ ofs += cnt + msg_len;
+ }
+
+ //HA_ATOMIC_INC(b_peek(&buf, ofs));
+ ofs += ring->ofs;
+ //HA_RWLOCK_RDUNLOCK(LOGSRV_LOCK, &ring->lock);
+
+ if (!(flags & RING_WF_WAIT_MODE))
+ break;
+
+ /* pause 10ms before checking for new stuff */
+ usleep(10000);
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct ring *ring;
+ struct stat statbuf;
+ const char *arg0;
+ int fd;
+
+ arg0 = argv[0];
+ while (argc > 1 && argv[1][0] == '-') {
+ argc--; argv++;
+ if (strcmp(argv[0], "-f") == 0)
+ force = 1;
+ else if (strcmp(argv[0], "-l") == 0)
+ lfremap = 1;
+ else if (strcmp(argv[0], "-r") == 0)
+ repair = 1;
+ else if (strcmp(argv[0], "--") == 0)
+ break;
+ else
+ usage(1, arg0);
+ }
+
+ if (argc < 2)
+ usage(1, arg0);
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror("open()");
+ return 1;
+ }
+
+ if (fstat(fd, &statbuf) < 0) {
+ perror("fstat()");
+ return 1;
+ }
+
+ ring = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (ring == MAP_FAILED) {
+ perror("mmap()");
+ return 1;
+ }
+
+ return dump_ring(ring, ~0, 0);
+}
+
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */