/* 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 *const *services;
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(&service_list->services, services) {
struct service *service = *services;
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 *const *services;
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(&service_list->services, services) {
struct service *service = *services;
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;
}
}