MINOR: logs: startup-logs can use a shm for logging the reload
When compiled with USE_SHM_OPEN=1 the startup-logs are now able to use an shm which is used to keep the logs when switching to mworker wait mode. This allows to keep the failed reload logs. When allocating the startup-logs at first start of the process, haproxy will do a shm_open with a unique path using the PID of the process, the file is unlink immediatly so we don't let unwelcomed files be. The fd resulting from this shm is stored in the HAPROXY_STARTUPLOGS_FD environment variable so it can be mmap again when switching to wait mode. When forking children, the process is copying the mmap to a a mallocated ring so we never share the same memory section between the master and the workers. When switching to wait mode, the shm is not used anymore as it is also copied to a mallocated structure. This allow to use the "show startup-logs" command over the master CLI, to get the logs of the latest startup or reload. This way the logs of the latest failed reload are also kept. This is only activated on the linux-glibc target for now.
This commit is contained in:
parent
35df34223b
commit
eba6a54cd4
6
Makefile
6
Makefile
@ -57,6 +57,7 @@
|
||||
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
|
||||
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
||||
# USE_PTHREAD_EMULATION: replace pthread's rwlocks with ours
|
||||
# USE_SHM_OPEN : use shm_open() for the startup-logs
|
||||
#
|
||||
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
|
||||
# "USE_xxx=" (empty string). The list of enabled and disabled options for a
|
||||
@ -345,7 +346,8 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER \
|
||||
USE_CLOSEFROM USE_ZLIB USE_SLZ USE_CPU_AFFINITY USE_TFO USE_NS \
|
||||
USE_DL USE_RT USE_DEVICEATLAS USE_51DEGREES USE_WURFL USE_SYSTEMD \
|
||||
USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL USE_THREAD_DUMP \
|
||||
USE_EVPORTS USE_OT USE_QUIC USE_PROMEX USE_MEMORY_PROFILING
|
||||
USE_EVPORTS USE_OT USE_QUIC USE_PROMEX USE_MEMORY_PROFILING \
|
||||
USE_SHM_OPEN
|
||||
|
||||
#### Target system options
|
||||
# Depending on the target platform, some options are set, as well as some
|
||||
@ -382,7 +384,7 @@ ifeq ($(TARGET),linux-glibc)
|
||||
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
||||
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY \
|
||||
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
||||
USE_GETADDRINFO USE_BACKTRACE)
|
||||
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN)
|
||||
INSTALL = install -v
|
||||
endif
|
||||
|
||||
|
@ -100,7 +100,10 @@
|
||||
#define MAX_SYSLOG_LEN 1024
|
||||
#endif
|
||||
|
||||
/* 64kB to archive startup-logs seems way more than enough */
|
||||
/* 64kB to archive startup-logs seems way more than enough
|
||||
* /!\ Careful when changing this size, it is used in a shm when exec() from
|
||||
* mworker to wait mode.
|
||||
*/
|
||||
#ifndef STARTUP_LOG_SIZE
|
||||
#define STARTUP_LOG_SIZE 65536
|
||||
#endif
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#define ERR_CODE (ERR_RETRYABLE|ERR_FATAL|ERR_ABORT) /* mask */
|
||||
|
||||
extern struct ring *startup_logs;
|
||||
|
||||
/* These codes may be used by config parsing functions which detect errors and
|
||||
* which need to inform the upper layer about them. They are all prefixed with
|
||||
@ -123,6 +124,10 @@ void ha_notice(const char *fmt, ...)
|
||||
void qfprintf(FILE *out, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
|
||||
void startup_logs_init();
|
||||
struct ring *startup_logs_dup(struct ring *src);
|
||||
void startup_logs_free(struct ring *r);
|
||||
|
||||
#endif /* _HAPROXY_ERRORS_H */
|
||||
|
||||
/*
|
||||
|
192
src/errors.c
192
src/errors.c
@ -1,5 +1,9 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <haproxy/api.h>
|
||||
@ -15,7 +19,10 @@
|
||||
|
||||
/* A global buffer used to store all startup alerts/warnings. It will then be
|
||||
* retrieve on the CLI. */
|
||||
static struct ring *startup_logs = NULL;
|
||||
struct ring *startup_logs = NULL;
|
||||
#ifdef USE_SHM_OPEN
|
||||
static struct ring *shm_startup_logs = NULL;
|
||||
#endif
|
||||
|
||||
/* A thread local buffer used to store all alerts/warnings. It can be used to
|
||||
* retrieve them for CLI commands after startup.
|
||||
@ -28,6 +35,185 @@ static THREAD_LOCAL struct buffer usermsgs_buf = BUF_NULL;
|
||||
#define USERMSGS_CTX_BUFSIZE PATH_MAX
|
||||
static THREAD_LOCAL struct usermsgs_ctx usermsgs_ctx = { .str = BUF_NULL, };
|
||||
|
||||
#ifdef USE_SHM_OPEN
|
||||
|
||||
/* initialise an SHM for the startup logs and return its fd */
|
||||
static int startup_logs_new_shm()
|
||||
{
|
||||
char *path = NULL;
|
||||
int fd = -1;
|
||||
int flags;
|
||||
|
||||
/* create a unique path per PID so we don't collide with another
|
||||
process */
|
||||
memprintf(&path, "/haproxy_startup_logs_%d", getpid());
|
||||
fd = shm_open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1)
|
||||
goto error;
|
||||
shm_unlink(path);
|
||||
ha_free(&path);
|
||||
|
||||
if (ftruncate(fd, STARTUP_LOG_SIZE) == -1)
|
||||
goto error;
|
||||
|
||||
flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
goto error;
|
||||
flags &= ~FD_CLOEXEC;
|
||||
flags = fcntl(fd, F_SETFD, flags);
|
||||
if (flags == -1)
|
||||
goto error;
|
||||
|
||||
return fd;
|
||||
error:
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* mmap a startup-logs from a <fd>.
|
||||
* if <new> is set to one, initialize the buffer.
|
||||
* Returns the ring.
|
||||
*/
|
||||
static struct ring *startup_logs_from_fd(int fd, int new)
|
||||
{
|
||||
char *area;
|
||||
struct ring *r = NULL;
|
||||
|
||||
if (fd == -1)
|
||||
goto error;
|
||||
|
||||
area = mmap(NULL, STARTUP_LOG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (area == MAP_FAILED || area == NULL)
|
||||
goto error;
|
||||
|
||||
if (new)
|
||||
r = ring_make_from_area(area, STARTUP_LOG_SIZE);
|
||||
else
|
||||
r = ring_cast_from_area(area);
|
||||
|
||||
if (r == NULL)
|
||||
goto error;
|
||||
|
||||
shm_startup_logs = r; /* save the ptr so we can unmap later */
|
||||
|
||||
return r;
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a shm accross reexec of the master.
|
||||
*
|
||||
* During the startup of the master, a shm_open must be done and the FD saved
|
||||
* into the HAPROXY_STARTUPLOGS_FD environment variable.
|
||||
*
|
||||
* When forking workers, the child must use a copy of the shm, not the shm itself.
|
||||
*
|
||||
* Once in wait mode, the shm must be copied and closed.
|
||||
*
|
||||
*/
|
||||
void startup_logs_init()
|
||||
{
|
||||
struct ring *r = NULL;
|
||||
char *str_fd, *endptr;
|
||||
int fd = -1;
|
||||
|
||||
str_fd = getenv("HAPROXY_STARTUPLOGS_FD");
|
||||
if (str_fd) {
|
||||
fd = strtol(str_fd, &endptr, 10);
|
||||
if (*endptr != '\0')
|
||||
goto error;
|
||||
unsetenv("HAPROXY_STARTUPLOGS_FD");
|
||||
}
|
||||
|
||||
/* during startup, or just after a reload.
|
||||
* Note: the WAIT_ONLY env variable must be
|
||||
* check in case of an early call */
|
||||
if (!(global.mode & MODE_MWORKER_WAIT) &&
|
||||
getenv("HAPROXY_MWORKER_WAIT_ONLY") == NULL) {
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
||||
fd = startup_logs_new_shm();
|
||||
if (fd == -1)
|
||||
goto error;
|
||||
|
||||
r = startup_logs_from_fd(fd, 1);
|
||||
if (!r)
|
||||
goto error;
|
||||
|
||||
memprintf(&str_fd, "%d", fd);
|
||||
setenv("HAPROXY_STARTUPLOGS_FD", str_fd, 1);
|
||||
ha_free(&str_fd);
|
||||
|
||||
} else {
|
||||
/* in wait mode, copy the shm to an allocated buffer */
|
||||
struct ring *prev = NULL;
|
||||
|
||||
if (fd == -1)
|
||||
goto error;
|
||||
|
||||
prev = startup_logs_from_fd(fd, 0);
|
||||
if (!prev)
|
||||
goto error;
|
||||
|
||||
r = startup_logs_dup(prev);
|
||||
if (!r)
|
||||
goto error;
|
||||
startup_logs_free(prev);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
startup_logs = r;
|
||||
|
||||
return;
|
||||
error:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
/* couldn't get a mmap to work */
|
||||
startup_logs = ring_new(STARTUP_LOG_SIZE);
|
||||
|
||||
}
|
||||
|
||||
#else /* ! USE_SHM_OPEN */
|
||||
|
||||
void startup_logs_init()
|
||||
{
|
||||
startup_logs = ring_new(STARTUP_LOG_SIZE);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* free the startup logs, unmap if it was an shm */
|
||||
void startup_logs_free(struct ring *r)
|
||||
{
|
||||
#ifdef USE_SHM_OPEN
|
||||
if (r == shm_startup_logs)
|
||||
munmap(r, STARTUP_LOG_SIZE);
|
||||
else
|
||||
#endif /* ! USE_SHM_OPEN */
|
||||
ring_free(r);
|
||||
}
|
||||
|
||||
/* duplicate a startup logs which was previously allocated in a shm */
|
||||
struct ring *startup_logs_dup(struct ring *src)
|
||||
{
|
||||
struct ring *dst = NULL;
|
||||
|
||||
/* must use the size of the previous buffer */
|
||||
dst = ring_new(b_size(&src->buf));
|
||||
if (!dst)
|
||||
goto error;
|
||||
|
||||
b_reset(&dst->buf);
|
||||
b_ncat(&dst->buf, &src->buf, b_data(&src->buf));
|
||||
error:
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* Put msg in usermsgs_buf.
|
||||
*
|
||||
* The message should not be terminated by a newline because this function
|
||||
@ -200,7 +386,7 @@ static void print_message(int use_usermsgs_ctx, const char *label, const char *f
|
||||
|
||||
if (global.mode & MODE_STARTING) {
|
||||
if (unlikely(!startup_logs))
|
||||
startup_logs = ring_new(STARTUP_LOG_SIZE);
|
||||
startup_logs_init();
|
||||
|
||||
if (likely(startup_logs)) {
|
||||
struct ist m[3];
|
||||
@ -361,7 +547,7 @@ static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx
|
||||
|
||||
/* register cli keywords */
|
||||
static struct cli_kw_list cli_kws = {{ },{
|
||||
{ { "show", "startup-logs", NULL }, "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
|
||||
{ { "show", "startup-logs", NULL }, "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL, NULL, ACCESS_MASTER },
|
||||
{{},}
|
||||
}};
|
||||
|
||||
|
@ -1909,6 +1909,8 @@ static void init(int argc, char **argv)
|
||||
struct pre_check_fct *prcf;
|
||||
int ideal_maxconn;
|
||||
|
||||
startup_logs_init();
|
||||
|
||||
if (!init_trash_buffers(1)) {
|
||||
ha_alert("failed to initialize trash buffers.\n");
|
||||
exit(1);
|
||||
@ -3431,9 +3433,13 @@ int main(int argc, char **argv)
|
||||
|
||||
/* the father launches the required number of processes */
|
||||
if (!(global.mode & MODE_MWORKER_WAIT)) {
|
||||
struct ring *tmp_startup_logs = NULL;
|
||||
|
||||
if (global.mode & MODE_MWORKER)
|
||||
mworker_ext_launch_all();
|
||||
|
||||
/* at this point the worker must have his own startup_logs buffer */
|
||||
tmp_startup_logs = startup_logs_dup(startup_logs);
|
||||
ret = fork();
|
||||
if (ret < 0) {
|
||||
ha_alert("[%s.main()] Cannot fork.\n", argv[0]);
|
||||
@ -3441,6 +3447,8 @@ int main(int argc, char **argv)
|
||||
exit(1); /* there has been an error */
|
||||
}
|
||||
else if (ret == 0) { /* child breaks here */
|
||||
startup_logs_free(startup_logs);
|
||||
startup_logs = tmp_startup_logs;
|
||||
/* This one must not be exported, it's internal! */
|
||||
unsetenv("HAPROXY_MWORKER_REEXEC");
|
||||
ha_random_jump96(1);
|
||||
|
Loading…
Reference in New Issue
Block a user