diff options
Diffstat (limited to 'src/master/service-log.c')
-rw-r--r-- | src/master/service-log.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/master/service-log.c b/src/master/service-log.c new file mode 100644 index 0000000..f5af931 --- /dev/null +++ b/src/master/service-log.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "common.h" +#include "array.h" +#include "aqueue.h" +#include "hash.h" +#include "ioloop.h" +#include "service.h" +#include "service-process.h" +#include "service-process-notify.h" +#include "service-anvil.h" +#include "service-log.h" + +#include <unistd.h> + +static int service_log_fds_init(const char *log_prefix, int log_fd[2], + buffer_t *handshake_buf) +{ + struct log_service_handshake handshake; + ssize_t ret; + + i_assert(log_fd[0] == -1); + + if (pipe(log_fd) < 0) { + i_error("pipe() failed: %m"); + return -1; + } + fd_close_on_exec(log_fd[0], TRUE); + fd_close_on_exec(log_fd[1], TRUE); + + i_zero(&handshake); + handshake.log_magic = MASTER_LOG_MAGIC; + handshake.prefix_len = strlen(log_prefix); + + buffer_set_used_size(handshake_buf, 0); + buffer_append(handshake_buf, &handshake, sizeof(handshake)); + buffer_append(handshake_buf, log_prefix, strlen(log_prefix)); + + ret = write(log_fd[1], handshake_buf->data, handshake_buf->used); + if (ret < 0) { + i_error("write(log handshake) failed: %m"); + return -1; + } + if ((size_t)ret != handshake_buf->used) { + i_error("write(log handshake) didn't write everything"); + return -1; + } + return 0; +} + +static int +service_process_write_log_bye(int fd, struct service_process *process) +{ + const char *data; + + if (process->service->log_process_internal_fd == -1) { + /* another log process was just destroyed */ + return 0; + } + + data = t_strdup_printf("%d %s BYE\n", + process->service->log_process_internal_fd, + dec2str(process->pid)); + if (write(fd, data, strlen(data)) < 0) { + if (errno != EAGAIN) + i_error("write(log process) failed: %m"); + return -1; + } + return 0; +} + +int services_log_init(struct service_list *service_list) +{ + struct service *service; + const char *log_prefix; + buffer_t *handshake_buf; + ssize_t ret = 0; + int fd; + + handshake_buf = buffer_create_dynamic(default_pool, 256); + if (service_log_fds_init(MASTER_LOG_PREFIX_NAME, + service_list->master_log_fd, + handshake_buf) < 0) + ret = -1; + else + fd_set_nonblock(service_list->master_log_fd[1], TRUE); + + i_assert(service_list->log_byes == NULL); + service_list->log_byes = + service_process_notify_init(service_list->master_log_fd[1], + service_process_write_log_bye); + + fd = MASTER_LISTEN_FD_FIRST + 1; + array_foreach_elem(&service_list->services, service) { + if (service->type == SERVICE_TYPE_LOG) + continue; + + log_prefix = t_strconcat(service->set->name, ": ", NULL); + if (service_log_fds_init(log_prefix, service->log_fd, + handshake_buf) < 0) { + ret = -1; + break; + } + service->log_process_internal_fd = fd++; + } + + buffer_free(&handshake_buf); + if (ret < 0) { + services_log_deinit(service_list); + return -1; + } + + service_anvil_send_log_fd(); + return 0; +} + +void services_log_deinit(struct service_list *service_list) +{ + struct service *const *services; + unsigned int i, count; + + services = array_get(&service_list->services, &count); + for (i = 0; i < count; i++) { + if (services[i]->log_fd[0] != -1) { + if (close(services[i]->log_fd[0]) < 0) { + service_error(services[i], + "close(log_fd) failed: %m"); + } + if (close(services[i]->log_fd[1]) < 0) { + service_error(services[i], + "close(log_fd) failed: %m"); + } + services[i]->log_fd[0] = -1; + services[i]->log_fd[1] = -1; + services[i]->log_process_internal_fd = -1; + } + } + if (service_list->log_byes != NULL) + service_process_notify_deinit(&service_list->log_byes); + if (service_list->master_log_fd[0] != -1) { + if (close(service_list->master_log_fd[0]) < 0) + i_error("close(master log fd) failed: %m"); + if (close(service_list->master_log_fd[1]) < 0) + i_error("close(master log fd) failed: %m"); + service_list->master_log_fd[0] = -1; + service_list->master_log_fd[1] = -1; + } +} + +void services_log_dup2(ARRAY_TYPE(dup2) *dups, + struct service_list *service_list, + unsigned int first_fd, unsigned int *fd_count) +{ + struct service *service; + unsigned int n = 0; + + /* master log fd is always the first one */ + dup2_append(dups, service_list->master_log_fd[0], first_fd); + n++; *fd_count += 1; + + array_foreach_elem(&service_list->services, service) { + if (service->log_fd[1] == -1) + continue; + + i_assert((int)(first_fd + n) == service->log_process_internal_fd); + dup2_append(dups, service->log_fd[0], first_fd + n); + n++; *fd_count += 1; + } +} |