ring <ringname>
Creates a new ring-buffer with name <ringname>.
+backing-file <path>
+ This replaces the regular memory allocation by a RAM-mapped file to store the
+ ring. This can be useful for collecting traces or logs for post-mortem
+ analysis, without having to attach a slow client to the CLI. Newer contents
+ will automatically replace older ones so that the latest contents are always
+ available. The contents written to the ring will be visible in that file once
+ the process stops (most often they will even be seen very soon after but
+ there is no such guarantee since writes are not synchronous).
+
+ When this option is used, the total storage area is reduced by the size of
+ the "struct ring" that starts at the beginning of the area, and that is
+ required to recover the area's contents. The file will be created with the
+ starting user's ownership, with mode 0600 and will be of the size configured
+ by the "size" directive.
+
+ WARNING: there are stability and security implications in using this feature.
+ First, backing the ring to a slow device (e.g. physical hard drive) may cause
+ perceptible slowdowns during accesses, and possibly even panics if too many
+ threads compete for accesses. Second, an external process modifying the area
+ could cause the haproxy process to crash or to overwrite some of its own
+ memory with traces. Third, if the file system fills up before the ring,
+ writes to the ring may cause the process to crash.
+
+ The information present in this ring are structured and are NOT directly
+ readable using a text editor (even though most of it looks barely readable).
+ The output of this file is only intended for developers.
+
description <text>
The description is an optional description string of the ring. It will
appear on CLI. By default, <name> is reused to fill this field.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+
#include <import/ist.h>
#include <haproxy/api.h>
#include <haproxy/applet.h>
goto err;
}
+ if (cfg_sink->store) {
+ ha_alert("parsing [%s:%d] : cannot resize an already mapped file, please specify 'size' before 'backing-file'.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto err;
+ }
+
if (size < cfg_sink->ctx.ring->buf.size) {
ha_warning("parsing [%s:%d] : ignoring new size '%llu' that is smaller than current size '%llu' for ring '%s'.\n",
file, linenum, (ullong)size, (ullong)cfg_sink->ctx.ring->buf.size, cfg_sink->name);
goto err;
}
}
+ else if (strcmp(args[0], "backing-file") == 0) {
+ /* This tries to mmap file <file> for size <size> and to use it as a backing store
+ * for ring <ring>. Existing data are delete. NULL is returned on error.
+ */
+ const char *backing = args[1];
+ size_t size;
+ void *area;
+ int fd;
+
+ if (!cfg_sink || (cfg_sink->type != SINK_TYPE_BUFFER)) {
+ ha_alert("parsing [%s:%d] : 'backing-file' only usable with existing rings.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto err;
+ }
+
+ if (cfg_sink->store) {
+ ha_alert("parsing [%s:%d] : 'backing-file' already specified for ring '%s' (was '%s').\n", file, linenum, cfg_sink->name, cfg_sink->store);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto err;
+ }
+
+ fd = open(backing, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ ha_alert("parsing [%s:%d] : cannot open backing-file '%s' for ring '%s': %s.\n", file, linenum, backing, cfg_sink->name, strerror(errno));
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto err;
+ }
+
+ size = (cfg_sink->ctx.ring->buf.size + 4095UL) & -4096UL;
+ if (ftruncate(fd, size) != 0) {
+ close(fd);
+ ha_alert("parsing [%s:%d] : could not adjust size of backing-file for ring '%s': %s.\n", file, linenum, cfg_sink->name, strerror(errno));
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto err;
+ }
+
+ area = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (area == MAP_FAILED) {
+ close(fd);
+ ha_alert("parsing [%s:%d] : failed to use '%s' as a backing file for ring '%s': %s.\n", file, linenum, backing, cfg_sink->name, strerror(errno));
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto err;
+ }
+
+ /* we don't need the file anymore */
+ close(fd);
+ cfg_sink->store = strdup(backing);
+
+ /* never fails */
+ ring_free(cfg_sink->ctx.ring);
+ cfg_sink->ctx.ring = ring_make_from_area(area, size);
+ }
else if (strcmp(args[0],"server") == 0) {
err_code |= parse_server(file, linenum, args, cfg_sink->forward_px, NULL,
SRV_PARSE_PARSE_ADDR|SRV_PARSE_INITIAL_RESOLVE);
struct sink *sink, *sb;
list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
- if (sink->type == SINK_TYPE_BUFFER)
- ring_free(sink->ctx.ring);
+ if (sink->type == SINK_TYPE_BUFFER) {
+ if (sink->store)
+ munmap(sink->ctx.ring->buf.area, sink->ctx.ring->buf.size);
+ else
+ ring_free(sink->ctx.ring);
+ }
LIST_DELETE(&sink->sink_list);
free(sink->name);
free(sink->desc);