From: Timo Sirainen Date: Tue, 5 May 2009 00:24:57 +0000 (-0400) Subject: master: Fixes to handling logging. X-Git-Tag: 2.0.alpha1~838 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=78fa3c578c14ee8a612f86cf73b6181c7f16463f;p=thirdparty%2Fdovecot%2Fcore.git master: Fixes to handling logging. Master now has a non-blocking write pipe to log process, so it no longer blocks if log process is hanging. Also it's cleaner to send log commands via a pipe specifically meant for them. --HG-- branch : HEAD --- diff --git a/src/lib-master/master-interface.h b/src/lib-master/master-interface.h index 600173bbbd..9334a34afd 100644 --- a/src/lib-master/master-interface.h +++ b/src/lib-master/master-interface.h @@ -28,6 +28,7 @@ struct log_service_handshake { unsigned int max_lines_per_sec; /* Add this previs to each logged line */ +#define MASTER_LOG_PREFIX_NAME "MASTER" unsigned int prefix_len; /* unsigned char prefix[]; */ }; diff --git a/src/lib-master/master-service.c b/src/lib-master/master-service.c index 871c504398..9670027768 100644 --- a/src/lib-master/master-service.c +++ b/src/lib-master/master-service.c @@ -64,9 +64,20 @@ master_service_init(const char *name, enum master_service_flags flags, int argc, char *argv[]) { struct master_service *service; + const char *str; i_assert(name != NULL); +#ifdef DEBUG + if (getenv("GDB") == NULL) { + int count; + + str = getenv("SOCKET_COUNT"); + count = str == NULL ? 0 : atoi(str); + fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024); + } +#endif + /* NOTE: we start rooted, so keep the code minimal until restrict_access_by_env() is called */ lib_init(); @@ -95,6 +106,9 @@ master_service_init(const char *name, enum master_service_flags flags, } else { service->version_string = PACKAGE_VERSION; } + str = getenv("SOCKET_COUNT"); + if (str != NULL) + service->socket_count = atoi(str); /* set up some kind of logging until we know exactly how and where we want to log */ diff --git a/src/log/log-connection.c b/src/log/log-connection.c index fb9beb9570..535aa286c0 100644 --- a/src/log/log-connection.c +++ b/src/log/log-connection.c @@ -1,6 +1,7 @@ /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */ #include "common.h" +#include "array.h" #include "ioloop.h" #include "llist.h" #include "hash.h" @@ -8,6 +9,8 @@ #include "master-service.h" #include "log-connection.h" +#include +#include #include #define FATAL_QUEUE_TIMEOUT_MSECS 500 @@ -26,10 +29,12 @@ struct log_connection { char *prefix; struct hash_table *clients; + unsigned int master:1; unsigned int handshaked:1; }; static struct log_connection *log_connections = NULL; +static ARRAY_DEFINE(logs_by_fd, struct log_connection *); static struct log_client *log_client_get(struct log_connection *log, pid_t pid) { @@ -52,47 +57,60 @@ static void log_parse_ip(struct log_connection *log, (void)net_addr2ip(failure->text + 3, &client->ip); } -static void log_remove_pid(struct log_connection *log, pid_t pid) -{ - struct log_client *client; - - client = hash_table_lookup(log->clients, POINTER_CAST(pid)); - if (client != NULL) { - hash_table_remove(log->clients, POINTER_CAST(pid)); - i_free(client); - } -} - static void log_parse_option(struct log_connection *log, const struct failure_line *failure) { if (strncmp(failure->text, "ip=", 3) == 0) log_parse_ip(log, failure); - else if (strcmp(failure->text, "bye") == 0) - log_remove_pid(log, failure->pid); } -static bool -log_handle_seen_fatal(struct log_connection *log, const char **_text) +static void log_parse_master_line(const char *line) { - const char *text = *_text; + struct log_connection *const *logs, *log; struct log_client *client; - pid_t pid = 0; - - while (*text >= '0' && *text <= '9') { - pid = pid*10 + (*text - '0'); - text++; + const char *p, *p2; + unsigned int count; + int service_fd; + long pid; + + p = strchr(line, ' '); + if (p == NULL || (p2 = strchr(++p, ' ')) == NULL) { + i_error("Received invalid input from master: %s", line); + return; } - if (*text != ' ' || pid == 0) - return FALSE; - *_text = text; + service_fd = atoi(t_strcut(line, ' ')); + pid = strtol(t_strcut(p, ' '), NULL, 10); + logs = array_get(&logs_by_fd, &count); + if (service_fd >= (int)count || logs[service_fd] == NULL) { + i_error("Received master input for invalid service_fd %d: %s", + service_fd, line); + return; + } + log = logs[service_fd]; client = hash_table_lookup(log->clients, POINTER_CAST(pid)); - if (client != NULL && client->fatal_logged) { - log_remove_pid(log, pid); - return TRUE; + line = p2 + 1; + + if (strcmp(line, "BYE") == 0) { + if (client == NULL) { + /* we haven't seen anything important from this client. + it's not an error. */ + return; + } + hash_table_remove(log->clients, POINTER_CAST(pid)); + i_free(client); + } else if (strncmp(line, "DEFAULT-FATAL ", 14) == 0) { + /* If the client has logged a fatal/panic, don't log this + message. */ + if (client == NULL || !client->fatal_logged) + i_error("%s", line + 14); + else { + hash_table_remove(log->clients, POINTER_CAST(pid)); + i_free(client); + } + } else { + i_error("Received unknown command from master: %s", line); } - return FALSE; } static void log_it(struct log_connection *log, const char *line) @@ -100,6 +118,11 @@ static void log_it(struct log_connection *log, const char *line) struct failure_line failure; struct log_client *client; + if (log->master) { + log_parse_master_line(line); + return; + } + i_failure_parse_line(line, &failure); switch (failure.log_type) { case LOG_TYPE_FATAL: @@ -107,20 +130,6 @@ static void log_it(struct log_connection *log, const char *line) client = log_client_get(log, failure.pid); client->fatal_logged = TRUE; break; - case LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL: - /* Special case for master connection. If the following PID - has logged a fatal/panic, don't log this message. */ - failure.log_type = LOG_TYPE_ERROR; - if (failure.pid != master_pid) { - i_error("Non-master process %s " - "sent LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL", - dec2str(failure.pid)); - break; - } - - if (log_handle_seen_fatal(log, &failure.text)) - return; - break; case LOG_TYPE_OPTION: log_parse_option(log, &failure); return; @@ -151,6 +160,14 @@ static bool log_connection_handshake(struct log_connection *log, handshake.prefix_len); *data += sizeof(handshake) + handshake.prefix_len; } + if (strcmp(log->prefix, MASTER_LOG_PREFIX_NAME) == 0) { + if (log->fd != MASTER_LISTEN_FD_FIRST) { + i_error("Received master prefix in handshake " + "from non-master fd %d", log->fd); + return FALSE; + } + log->master = TRUE; + } log->handshaked = TRUE; return TRUE; } @@ -171,7 +188,7 @@ static void log_connection_input(struct log_connection *log) line = data; if (!log->handshaked) - log_connection_handshake(log, &line, ret); + (void)log_connection_handshake(log, &line, ret); p = line; while ((p = strchr(line, '\n')) != NULL) { @@ -194,6 +211,7 @@ struct log_connection *log_connection_create(int fd) log->io = io_add(fd, IO_READ, log_connection_input, log); log->clients = hash_table_create(default_pool, default_pool, 0, NULL, NULL); + array_idx_set(&logs_by_fd, fd, &log); DLLIST_PREPEND(&log_connections, log); log_connection_input(log); @@ -205,6 +223,8 @@ void log_connection_destroy(struct log_connection *log) struct hash_iterate_context *iter; void *key, *value; + array_idx_clear(&logs_by_fd, log->fd); + DLLIST_REMOVE(&log_connections, log); iter = hash_table_iterate_init(log->clients); @@ -215,16 +235,24 @@ void log_connection_destroy(struct log_connection *log) if (log->io != NULL) io_remove(&log->io); + if (close(log->fd) < 0) + i_error("close(log connection fd) failed: %m"); i_free(log->prefix); i_free(log); master_service_client_connection_destroyed(service); } +void log_connections_init(void) +{ + i_array_init(&logs_by_fd, 64); +} + void log_connections_deinit(void) { /* normally we don't exit until all log connections are gone, but we could get here when we're being killed by a signal */ while (log_connections != NULL) log_connection_destroy(log_connections); + array_free(&logs_by_fd); } diff --git a/src/log/log-connection.h b/src/log/log-connection.h index 819bb2012a..c2abbe2d82 100644 --- a/src/log/log-connection.h +++ b/src/log/log-connection.h @@ -4,6 +4,7 @@ struct log_connection *log_connection_create(int fd); void log_connection_destroy(struct log_connection *log); +void log_connections_init(void); void log_connections_deinit(void); #endif diff --git a/src/log/main.c b/src/log/main.c index 7434382c0f..d8496ee964 100644 --- a/src/log/main.c +++ b/src/log/main.c @@ -31,6 +31,7 @@ static void main_init(void) lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL); master_pid = getppid(); + log_connections_init(); } static void main_deinit(void) diff --git a/src/master/service-log.c b/src/master/service-log.c index c1dc3fbfe4..c8f4421f1b 100644 --- a/src/master/service-log.c +++ b/src/master/service-log.c @@ -2,61 +2,86 @@ #include "common.h" #include "array.h" +#include "aqueue.h" #include "hash.h" #include "ioloop.h" #include "fd-close-on-exec.h" +#include "fd-set-nonblock.h" #include "service.h" #include "service-process.h" #include "service-log.h" #include -int services_log_init(struct service_list *service_list) +static int service_log_fds_init(const char *log_prefix, int log_fd[2], + buffer_t *handshake_buf) { struct log_service_handshake handshake; - struct service *const *services; - unsigned int i, count; - buffer_t *handshake_buf; - ssize_t ret = 0; + 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); memset(&handshake, 0, sizeof(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; +} + +int services_log_init(struct service_list *service_list) +{ + struct service *const *services; + unsigned int i, count, n; + const char *log_prefix; + buffer_t *handshake_buf; + ssize_t ret = 0; handshake_buf = buffer_create_dynamic(default_pool, 256); services = array_get(&service_list->services, &count); + + 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); + + n = 1; for (i = 0; i < count; i++) { if (services[i]->type == SERVICE_TYPE_LOG) continue; - i_assert(services[i]->log_fd[0] == -1); - if (pipe(services[i]->log_fd) < 0) { - i_error("pipe() failed: %m"); - ret = -1; - break; - } - fd_close_on_exec(services[i]->log_fd[0], TRUE); - fd_close_on_exec(services[i]->log_fd[1], TRUE); - - handshake.prefix_len = strlen(services[i]->set->name) + 2; - - buffer_set_used_size(handshake_buf, 0); - buffer_append(handshake_buf, &handshake, sizeof(handshake)); - buffer_append(handshake_buf, services[i]->set->name, - strlen(services[i]->set->name)); - buffer_append(handshake_buf, ": ", 2); - - ret = write(services[i]->log_fd[1], - handshake_buf->data, handshake_buf->used); - if (ret < 0) { - i_error("write(log handshake) failed: %m"); - break; - } - if ((size_t)ret != handshake_buf->used) { - i_error("write(log handshake) didn't write everything"); + log_prefix = t_strconcat(services[i]->set->name, ": ", NULL); + if (service_log_fds_init(log_prefix, + services[i]->log_fd, + handshake_buf) < 0) { ret = -1; break; } + services[i]->log_process_internal_fd = + MASTER_LISTEN_FD_FIRST + n++; } + buffer_free(&handshake_buf); if (ret < 0) { services_log_deinit(service_list); @@ -65,19 +90,24 @@ int services_log_init(struct service_list *service_list) return 0; } -static void service_remove_log_io_writes(struct service *service) +void services_log_clear_byes(struct service_list *service_list) { - struct hash_iterate_context *iter; - void *key, *value; + struct service_process *const *processes, *process; + unsigned int i, count; - iter = hash_table_iterate_init(service->list->pids); - while (hash_table_iterate(iter, &key, &value)) { - struct service_process *process = value; + if (service_list->io_log_write == NULL) + return; - if (process->io_log_write != NULL) - io_remove(&process->io_log_write); + processes = array_idx_modifiable(&service_list->bye_arr, 0); + count = aqueue_count(service_list->bye_queue); + for (i = 0; i < count; i++) { + process = processes[aqueue_idx(service_list->bye_queue, i)]; + service_process_unref(process); } - hash_table_iterate_deinit(&iter); + aqueue_clear(service_list->bye_queue); + array_clear(&service_list->bye_arr); + + io_remove(&service_list->io_log_write); } void services_log_deinit(struct service_list *service_list) @@ -98,9 +128,18 @@ void services_log_deinit(struct service_list *service_list) } services[i]->log_fd[0] = -1; services[i]->log_fd[1] = -1; - service_remove_log_io_writes(services[i]); + services[i]->log_process_internal_fd = -1; } } + services_log_clear_byes(service_list); + 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, @@ -108,13 +147,19 @@ void services_log_dup2(ARRAY_TYPE(dup2) *dups, unsigned int first_fd, unsigned int *fd_count) { struct service *const *services; - unsigned int i, n, count; + unsigned int i, n = 0, count; + + /* master log fd is always the first one */ + dup2_append(dups, service_list->master_log_fd[0], first_fd); + n++; *fd_count += 1; services = array_get(&service_list->services, &count); - for (i = n = 0; i < count; i++) { - if (services[i]->log_fd[1] != -1) { - dup2_append(dups, services[i]->log_fd[0], first_fd + n); - n++; *fd_count += 1; - } + for (i = 0; i < count; i++) { + if (services[i]->log_fd[1] == -1) + continue; + + i_assert((int)(first_fd + n) == services[i]->log_process_internal_fd); + dup2_append(dups, services[i]->log_fd[0], first_fd + n); + n++; *fd_count += 1; } } diff --git a/src/master/service-log.h b/src/master/service-log.h index 475c91dd14..e6dca287eb 100644 --- a/src/master/service-log.h +++ b/src/master/service-log.h @@ -6,6 +6,7 @@ int services_log_init(struct service_list *service_list); void services_log_deinit(struct service_list *service_list); +void services_log_clear_byes(struct service_list *service_list); void services_log_dup2(ARRAY_TYPE(dup2) *dups, struct service_list *service_list, unsigned int first_fd, unsigned int *fd_count); diff --git a/src/master/service-process.c b/src/master/service-process.c index eb78edbba2..ae56a9aa28 100644 --- a/src/master/service-process.c +++ b/src/master/service-process.c @@ -2,6 +2,7 @@ #include "common.h" #include "array.h" +#include "aqueue.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" @@ -27,12 +28,12 @@ #include #include -static const char ** +static void service_dup_fds(struct service *service, int auth_fd, int std_fd) { struct service_listener *const *listeners; ARRAY_TYPE(dup2) dups; - unsigned int i, count, n, socket_listener_count; + unsigned int i, count, n = 0, socket_listener_count; /* stdin/stdout is already redirected to /dev/null. Other master fds should have been opened with fd_close_on_exec() so we don't have to @@ -43,8 +44,16 @@ service_dup_fds(struct service *service, int auth_fd, int std_fd) socket_listener_count = 0; listeners = array_get(&service->listeners, &count); - t_array_init(&dups, count + 4); - for (i = n = 0; i < count; i++) { + t_array_init(&dups, count + 10); + + if (service->type == SERVICE_TYPE_LOG) { + i_assert(n == 0); + services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST, + &socket_listener_count); + n += socket_listener_count; + } + + for (i = 0; i < count; i++) { if (listeners[i]->fd == -1) continue; @@ -63,11 +72,6 @@ service_dup_fds(struct service *service, int auth_fd, int std_fd) dup2_append(&dups, auth_fd, MASTER_AUTH_FD); env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD)); break; - case SERVICE_TYPE_LOG: - services_log_dup2(&dups, service->list, - MASTER_LISTEN_FD_FIRST + n, - &socket_listener_count); - /* fall through */ default: i_assert(auth_fd == -1); dup2_append(&dups, null_fd, MASTER_AUTH_FD); @@ -101,18 +105,7 @@ service_dup_fds(struct service *service, int auth_fd, int std_fd) if (dup2_array(&dups) < 0) service_error(service, "dup2s failed"); -#ifdef DEBUG env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count)); -#endif - - if (socket_listener_count == 1) - return NULL; - else { - const char **args = t_new(const char *, 3); - args[0] = "-s"; - args[1] = dec2str(socket_listener_count); - return args; - } } static int validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid, @@ -338,8 +331,6 @@ service_process_create(struct service *service, const char *const *auth_args, } if (pid == 0) { /* child */ - const char **args; - if (fd[0] != -1) (void)close(fd[0]); service_process_setup_environment(service, uid); @@ -349,9 +340,9 @@ service_process_create(struct service *service, const char *const *auth_args, base64_encode(data, data_size, str); env_put(str_c(str)); } - args = service_dup_fds(service, fd[1], std_fd); + service_dup_fds(service, fd[1], std_fd); drop_privileges(service, auth_args); - process_exec(service->executable, args); + process_exec(service->executable, NULL); } switch (service->type) { @@ -395,31 +386,62 @@ service_process_create(struct service *service, const char *const *auth_args, return process; } -static void service_process_log_bye(struct service_process *process) +static int service_process_write_bye(struct service_process *process) { const char *data; + data = t_strdup_printf("%d %s BYE\n", + process->service->log_process_internal_fd, + dec2str(process->pid)); + if (write(process->service->list->master_log_fd[1], + data, strlen(data)) < 0) { + if (errno != EAGAIN) + i_error("write(log process) failed: %m"); + return -1; + } + return 0; +} + +static void service_list_log_flush_byes(struct service_list *service_list) +{ + struct service_process *const *processes, *process; + + while (aqueue_count(service_list->bye_queue) > 0) { + processes = array_idx_modifiable(&service_list->bye_arr, 0); + process = processes[aqueue_idx(service_list->bye_queue, 0)]; + + if (service_process_write_bye(process) < 0) { + if (errno != EAGAIN) + services_log_clear_byes(service_list); + return; + } + service_process_unref(process); + aqueue_delete_tail(service_list->bye_queue); + } + io_remove(&service_list->io_log_write); +} + +static void service_process_log_bye(struct service_process *process) +{ + struct service_list *service_list = process->service->list; + if (process->service->log_fd[1] == -1) { /* stopping all services */ return; } - data = t_strdup_printf("\001%c%s bye\n", - LOG_TYPE_OPTION+1, dec2str(process->pid)); - if (write(process->service->log_fd[1], data, strlen(data)) < 0) { + if (service_process_write_bye(process) < 0) { if (errno != EAGAIN) - i_error("write(log process) failed: %m"); - else { - process->io_log_write = - io_add(process->service->log_fd[1], IO_WRITE, - service_process_log_bye, process); - service_process_ref(process); - } - } else { - if (process->io_log_write != NULL) { - io_remove(&process->io_log_write); - service_process_unref(process); + return; + + if (service_list->io_log_write == NULL) { + service_list->io_log_write = + io_add(service_list->master_log_fd[1], IO_WRITE, + service_list_log_flush_byes, + service_list); } + aqueue_append(service_list->bye_queue, &process); + service_process_ref(process); } } @@ -468,7 +490,6 @@ int service_process_unref(struct service_process *process) if (--process->refcount > 0) return TRUE; - i_assert(process->io_log_write == NULL); i_assert(process->destroyed); i_free(process); @@ -583,11 +604,13 @@ static void service_process_log(struct service_process *process, /* log it via the log process in charge of handling this process's logging */ - data = t_strdup_printf("\001%c%s %s %s\n", - type+1, my_pid, dec2str(process->pid), str); - if (write(process->service->log_fd[1], data, strlen(data)) < 0) { + data = t_strdup_printf("%d %s DEFAULT-FATAL %s\n", + process->service->log_process_internal_fd, + dec2str(process->pid), str); + if (write(process->service->list->master_log_fd[1], + data, strlen(data)) < 0) { i_error("write(log process) failed: %m"); - i_log_type(type, "%s", str); + i_error("%s", str); } } diff --git a/src/master/service-process.h b/src/master/service-process.h index 61b011ea7c..aca151efce 100644 --- a/src/master/service-process.h +++ b/src/master/service-process.h @@ -19,8 +19,6 @@ struct service_process { /* kill the process if it doesn't send initial status notification */ struct timeout *to_status; - /* we're waiting to be able to send "bye" to log process */ - struct io *io_log_write; unsigned int destroyed:1; }; diff --git a/src/master/service.c b/src/master/service.c index b46f9792cf..270b3ffa6f 100644 --- a/src/master/service.c +++ b/src/master/service.c @@ -2,6 +2,7 @@ #include "common.h" #include "array.h" +#include "aqueue.h" #include "hash.h" #include "str.h" #include "service.h" @@ -238,6 +239,7 @@ service_create(pool_t pool, const struct service_settings *set, service->log_fd[1] = -1; service->status_fd[0] = -1; service->status_fd[1] = -1; + service->log_process_internal_fd = -1; if (array_is_created(&set->unix_listeners)) unix_listeners = array_get(&set->unix_listeners, &unix_count); @@ -336,6 +338,8 @@ services_create(const struct master_settings *set, service_list = p_new(pool, struct service_list, 1); service_list->pool = pool; service_list->child_process_env = child_process_env; + service_list->master_log_fd[0] = -1; + service_list->master_log_fd[1] = -1; service_settings = array_get(&set->services, &count); p_array_init(&service_list->services, pool, count); @@ -400,6 +404,8 @@ services_create(const struct master_settings *set, service_list->pids = hash_table_create(default_pool, pool, 0, pid_hash, pid_hash_cmp); + p_array_init(&service_list->bye_arr, pool, 64); + service_list->bye_queue = aqueue_init(&service_list->bye_arr.arr); return service_list; } @@ -440,5 +446,6 @@ void services_destroy(struct service_list *service_list) hash_table_iterate_deinit(&iter); hash_table_destroy(&service_list->pids); + aqueue_deinit(&service_list->bye_queue); pool_unref(&service_list->pool); } diff --git a/src/master/service.h b/src/master/service.h index bf4bd5794f..3bde297203 100644 --- a/src/master/service.h +++ b/src/master/service.h @@ -65,8 +65,11 @@ struct service { /* max number of processes allowed */ unsigned int process_limit; - /* log process pipe file descriptors */ + /* log process pipe file descriptors. */ int log_fd[2]; + /* fd that log process sees log_fd[0] as. can be used to identify + service name when sending commands via master_log_fd. */ + int log_process_internal_fd; /* status report pipe file descriptors */ int status_fd[2]; @@ -92,6 +95,14 @@ struct service_list { struct hash_table *pids; const char *const *child_process_env; + /* nonblocking log fds usd by master */ + int master_log_fd[2]; + /* we're waiting to be able to send "bye" to log process */ + struct io *io_log_write; + /* List of processes who are waiting for the "bye" */ + struct aqueue *bye_queue; + ARRAY_DEFINE(bye_arr, struct service_process *); + ARRAY_DEFINE(services, struct service *); };