service.c revision 7f6762146f515f9ccbab0876a7747df2fbd4a11e
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "common.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "ioloop.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "array.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "aqueue.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "hash.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "str.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "master-service.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "master-service-settings.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "service.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "service-anvil.h"
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen#include "service-process.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include "service-monitor.h"
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include <unistd.h>
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#include <signal.h>
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi#define SERVICE_DIE_TIMEOUT_MSECS (1000*60)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen#define SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS 2
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstruct hash_table *service_pids;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenvoid service_error(struct service *service, const char *format, ...)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen va_list args;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen va_start(args, format);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen i_error("service(%s): %s", service->set->name,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen t_strdup_vprintf(format, args));
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen va_end(args);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen}
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstatic struct service_listener *
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenservice_create_file_listener(struct service *service,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen enum service_listener_type type,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const struct file_listener_settings *set,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const char **error_r)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct service_listener *l;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen gid_t gid;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l = p_new(service->list->pool, struct service_listener, 1);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->service = service;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->type = type;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->fd = -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->set.fileset.set = set;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (get_uidgid(set->user, &l->set.fileset.uid, &gid, error_r) < 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (get_gid(set->group, &l->set.fileset.gid, error_r) < 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return l;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen}
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstatic int
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenresolve_ip(const char *address, struct ip_addr *ip_r, const char **error_r)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct ip_addr *ip_list;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen unsigned int ips_count;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen int ret;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (address == NULL || strcmp(address, "*") == 0) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* IPv4 any */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen net_get_ip_any4(ip_r);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return 0;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (strcmp(address, "::") == 0) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* IPv6 any */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen net_get_ip_any6(ip_r);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return 0;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* Return the first IP if there happens to be multiple. */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen ret = net_gethostbyname(address, &ip_list, &ips_count);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (ret != 0) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen *error_r = t_strdup_printf("Can't resolve address %s: %s",
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen address, net_gethosterror(ret));
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (ips_count < 1) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen *error_r = t_strdup_printf("No IPs for address: %s", address);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (ips_count > 1) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen *error_r = t_strdup_printf("Multiple IPs for address: %s",
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen address);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen *ip_r = ip_list[0];
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return 0;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen}
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstatic struct service_listener *
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenservice_create_one_inet_listener(struct service *service,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const struct inet_listener_settings *set,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const char *address, const char **error_r)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct service_listener *l;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen i_assert(set->port != 0);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l = p_new(service->list->pool, struct service_listener, 1);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->service = service;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->type = SERVICE_LISTENER_INET;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->fd = -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->set.inetset.set = set;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l->inet_address = p_strdup(service->list->pool, address);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (resolve_ip(address, &l->set.inetset.ip, error_r) < 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (set->port > 65535) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen *error_r = t_strdup_printf("Invalid port: %u", set->port);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return l;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen}
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstatic int
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenservice_create_inet_listeners(struct service *service,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const struct inet_listener_settings *set,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const char **error_r)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen static struct service_listener *l;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const char *const *tmp, *addresses;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen bool ssl_disabled = strcmp(service->set->master_set->ssl, "no") == 0;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (set->port == 0) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* disabled */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return 0;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (*set->address != '\0')
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen addresses = set->address;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen else {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* use the default listen address */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen addresses = service->set->master_set->listen;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen tmp = t_strsplit_spaces(addresses, ", ");
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen for (; *tmp != NULL; tmp++) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (set->ssl && ssl_disabled)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen continue;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l = service_create_one_inet_listener(service, set, *tmp,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen error_r);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (l == NULL)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen array_append(&service->listeners, &l, 1);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->have_inet_listeners = TRUE;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return 0;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen}
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstatic struct service *
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenservice_create(pool_t pool, const struct service_settings *set,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct service_list *service_list, const char **error_r)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct file_listener_settings *const *unix_listeners;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct file_listener_settings *const *fifo_listeners;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct inet_listener_settings *const *inet_listeners;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct service *service;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen struct service_listener *l;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const char *const *tmp;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen string_t *str;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen unsigned int i, unix_count, fifo_count, inet_count;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service = p_new(pool, struct service, 1);
debc93e622751c6c09e8105e504c5833f1ca0d6dMartti Rannanjärvi service->list = service_list;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->set = set;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->client_limit = set->client_limit != 0 ? set->client_limit :
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen set->master_set->default_client_limit;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (set->service_count > 0 &&
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen service->client_limit > set->service_count)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen service->client_limit = set->service_count;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->vsz_limit = set->vsz_limit != (uoff_t)-1 ? set->vsz_limit :
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen set->master_set->default_vsz_limit;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->type = service->set->parsed_type;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (set->process_limit == 0) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* unlimited */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->process_limit = INT_MAX;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen } else if (set->process_limit == -1U) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* use default */
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen service->process_limit =
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen set->master_set->default_process_limit;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen } else {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->process_limit = set->process_limit;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (set->executable == NULL) {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen *error_r = "executable not given";
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* default gid to user's primary group */
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (get_uidgid(set->user, &service->uid, &service->gid, error_r) < 0)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (*set->group != '\0') {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (get_gid(set->group, &service->gid, error_r) < 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (get_gid(set->privileged_group, &service->privileged_gid,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen error_r) < 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (*set->extra_groups != '\0') {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen str = t_str_new(64);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen tmp = t_strsplit(set->extra_groups, ",");
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen for (; *tmp != NULL; tmp++) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen gid_t gid;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (get_gid(*tmp, &gid, error_r) < 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (str_len(str) > 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen str_append_c(str, ',');
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen str_append(str, dec2str(gid));
2f16d2e0b4408370cd44db359759b23a8c0656d3Phil Carmody }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->extra_gids = p_strdup(pool, str_c(str));
2f16d2e0b4408370cd44db359759b23a8c0656d3Phil Carmody }
2f16d2e0b4408370cd44db359759b23a8c0656d3Phil Carmody
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (*set->executable == '/')
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->executable = set->executable;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen else {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->executable =
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen p_strconcat(pool, set->master_set->libexec_dir, "/",
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen set->executable, NULL);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* set these later, so if something fails we don't have to worry about
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen closing them */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->log_fd[0] = -1;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen service->log_fd[1] = -1;
9644b7914445f0fb1098038218bfcb7d135a8698Timo Sirainen service->status_fd[0] = -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->status_fd[1] = -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->log_process_internal_fd = -1;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->login_notify_fd = -1;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (service->type == SERVICE_TYPE_ANVIL) {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen service->status_fd[0] = service_anvil_global->status_fd[0];
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen service->status_fd[1] = service_anvil_global->status_fd[1];
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (array_is_created(&set->unix_listeners))
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen unix_listeners = array_get(&set->unix_listeners, &unix_count);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen else {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen unix_listeners = NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen unix_count = 0;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (array_is_created(&set->fifo_listeners))
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen fifo_listeners = array_get(&set->unix_listeners, &fifo_count);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen else {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen fifo_listeners = NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen fifo_count = 0;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (array_is_created(&set->inet_listeners))
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen inet_listeners = array_get(&set->inet_listeners, &inet_count);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen else {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen inet_listeners = NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen inet_count = 0;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (unix_count == 0 && service->type == SERVICE_TYPE_CONFIG) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen *error_r = "Service must have unix listeners";
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen p_array_init(&service->listeners, pool,
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen unix_count + fifo_count + inet_count);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen for (i = 0; i < unix_count; i++) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (unix_listeners[i]->mode == 0) {
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen /* disabled */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen continue;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen unix_listeners[i], error_r);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (l == NULL)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return NULL;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen array_append(&service->listeners, &l, 1);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen for (i = 0; i < fifo_count; i++) {
795aeec896095aa8f08cc5d3282c88cc0921bff6Timo Sirainen if (fifo_listeners[i]->mode == 0) {
795aeec896095aa8f08cc5d3282c88cc0921bff6Timo Sirainen /* disabled */
795aeec896095aa8f08cc5d3282c88cc0921bff6Timo Sirainen continue;
795aeec896095aa8f08cc5d3282c88cc0921bff6Timo Sirainen }
4394b73cacaf2c31a9b601f66b6e26a1c8f114b4Timo Sirainen
4394b73cacaf2c31a9b601f66b6e26a1c8f114b4Timo Sirainen l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
2ecee6ed2bfd5c9bc5c6cc8a675b9db4cbbcd81fTimo Sirainen fifo_listeners[i], error_r);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (l == NULL)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen array_append(&service->listeners, &l, 1);
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen for (i = 0; i < inet_count; i++) {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (service_create_inet_listeners(service, inet_listeners[i],
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen error_r) < 0)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (access(t_strcut(service->executable, ' '), X_OK) < 0) {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen *error_r = t_strdup_printf("access(%s) failed: %m",
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen t_strcut(service->executable, ' '));
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return service;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen}
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainenstatic unsigned int pid_hash(const void *p)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen{
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen const pid_t *pid = p;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return (unsigned int)*pid;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen}
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenstatic int pid_hash_cmp(const void *p1, const void *p2)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen{
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen const pid_t *pid1 = p1, *pid2 = p2;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
1df526903ed039e8ff966a223c43b8d04eddf3c7Phil Carmody return *pid1 < *pid2 ? -1 :
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen *pid1 > *pid2 ? 1 : 0;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen}
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainenstruct service *
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainenservice_lookup(struct service_list *service_list, const char *name)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen{
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen struct service *const *services;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen array_foreach(&service_list->services, services) {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen struct service *service = *services;
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen if (strcmp(service->set->name, name) == 0)
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainen return service;
}
return NULL;
}
struct service *
service_lookup_type(struct service_list *service_list, enum service_type type)
{
struct service *const *services;
array_foreach(&service_list->services, services) {
struct service *service = *services;
if (service->type == type)
return service;
}
return NULL;
}
static bool service_want(struct service_settings *set)
{
char *const *proto;
if (*set->protocol == '\0')
return TRUE;
for (proto = set->master_set->protocols_split; *proto != NULL; proto++) {
if (strcmp(*proto, set->protocol) == 0)
return TRUE;
}
return FALSE;
}
int services_create(const struct master_settings *set,
const char *const *child_process_env,
struct service_list **services_r, const char **error_r)
{
struct service_list *service_list;
struct service *service;
struct service_settings *const *service_settings;
pool_t pool;
const char *error;
unsigned int i, count;
pool = pool_alloconly_create("services pool", 4096);
service_list = p_new(pool, struct service_list, 1);
service_list->refcount = 1;
service_list->pool = pool;
service_list->service_set = master_service_settings_get(master_service);
service_list->set_pool = master_service_settings_detach(master_service);
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);
for (i = 0; i < count; i++) {
if (!service_want(service_settings[i]))
continue;
service = service_create(pool, service_settings[i],
service_list, &error);
if (service == NULL) {
*error_r = t_strdup_printf("service(%s) %s",
service_settings[i]->name, error);
return -1;
}
switch (service->type) {
case SERVICE_TYPE_LOG:
if (service_list->log != NULL) {
*error_r = "Multiple log services specified";
return -1;
}
service_list->log = service;
break;
case SERVICE_TYPE_CONFIG:
if (service_list->config != NULL) {
*error_r = "Multiple config services specified";
return -1;
}
service_list->config = service;
break;
case SERVICE_TYPE_ANVIL:
if (service_list->anvil != NULL) {
*error_r = "Multiple anvil services specified";
return -1;
}
service_list->anvil = service;
break;
default:
break;
}
array_append(&service_list->services, &service, 1);
}
if (service_list->log == NULL) {
*error_r = "log service not specified";
return -1;
}
if (service_list->config == NULL) {
*error_r = "config process not specified";
return -1;
}
*services_r = service_list;
return 0;
}
void service_signal(struct service *service, int signo)
{
struct service_process *process = service->processes;
for (; process != NULL; process = process->next) {
i_assert(process->service == service);
if (!SERVICE_PROCESS_IS_INITIALIZED(process) &&
signo != SIGKILL) {
/* too early to signal it */
continue;
}
if (kill(process->pid, signo) < 0 && errno != ESRCH) {
service_error(service, "kill(%s, %d) failed: %m",
dec2str(process->pid), signo);
}
}
}
static void service_login_notify_send(struct service *service)
{
service->last_login_notify_time = ioloop_time;
if (service->to_login_notify != NULL)
timeout_remove(&service->to_login_notify);
service_signal(service, SIGUSR1);
}
static void service_login_notify_timeout(struct service *service)
{
service_login_notify_send(service);
}
void service_login_notify(struct service *service, bool all_processes_full)
{
enum master_login_state state;
int diff;
if (service->last_login_full_notify == all_processes_full ||
service->login_notify_fd == -1)
return;
/* change the state always immediately. it's cheap. */
service->last_login_full_notify = all_processes_full;
state = all_processes_full ? MASTER_LOGIN_STATE_FULL :
MASTER_LOGIN_STATE_NONFULL;
if (lseek(service->login_notify_fd, state, SEEK_SET) < 0)
service_error(service, "lseek(notify fd) failed: %m");
/* but don't send signal to processes too often */
diff = ioloop_time - service->last_login_notify_time;
if (diff < SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS) {
if (service->to_login_notify != NULL)
return;
diff = (SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS - diff) * 1000;
service->to_login_notify =
timeout_add(diff, service_login_notify_timeout,
service);
} else {
service_login_notify_send(service);
}
}
static void services_kill_timeout(struct service_list *service_list)
{
struct service *const *services, *log_service;
bool sigterm_log;
int sig;
if (!service_list->sigterm_sent || !service_list->sigterm_sent_to_log)
sig = SIGTERM;
else
sig = SIGKILL;
sigterm_log = service_list->sigterm_sent;
service_list->sigterm_sent = TRUE;
i_warning("Processes aren't dying after reload, sending %s.",
sig == SIGTERM ? "SIGTERM" : "SIGKILL");
log_service = NULL;
array_foreach(&service_list->services, services) {
struct service *service = *services;
if (service->type == SERVICE_TYPE_LOG)
log_service = service;
else
service_signal(service, sig);
}
/* kill log service later so it could still have a chance of logging
something */
if (log_service != NULL && sigterm_log) {
service_signal(log_service, sig);
service_list->sigterm_sent_to_log = TRUE;
}
}
void services_destroy(struct service_list *service_list)
{
/* make sure we log if child processes died unexpectedly */
services_monitor_reap_children();
services_monitor_stop(service_list);
if (service_list->refcount > 1 &&
service_list->service_set->shutdown_clients) {
service_list->to_kill =
timeout_add(SERVICE_DIE_TIMEOUT_MSECS,
services_kill_timeout, service_list);
}
service_list->destroyed = TRUE;
service_list_unref(service_list);
}
void service_list_ref(struct service_list *service_list)
{
i_assert(service_list->refcount > 0);
service_list->refcount++;
}
void service_list_unref(struct service_list *service_list)
{
i_assert(service_list->refcount > 0);
if (--service_list->refcount > 0)
return;
if (service_list->to_kill != NULL)
timeout_remove(&service_list->to_kill);
pool_unref(&service_list->set_pool);
pool_unref(&service_list->pool);
}
const char *services_get_config_socket_path(struct service_list *service_list)
{
struct service_listener *const *listeners;
unsigned int count;
listeners = array_get(&service_list->config->listeners, &count);
i_assert(count > 0);
return listeners[0]->set.fileset.set->path;
}
static void service_throttle_timeout(struct service *service)
{
timeout_remove(&service->to_throttle);
service_monitor_listen_start(service);
}
void service_throttle(struct service *service, unsigned int secs)
{
if (service->to_throttle != NULL)
return;
service_monitor_listen_stop(service);
service->to_throttle = timeout_add(secs * 1000,
service_throttle_timeout, service);
}
void services_throttle_time_sensitives(struct service_list *list,
unsigned int secs)
{
struct service *const *services;
array_foreach(&list->services, services) {
struct service *service = *services;
if (service->type == SERVICE_TYPE_UNKNOWN)
service_throttle(service, secs);
}
}
void service_pids_init(void)
{
service_pids = hash_table_create(default_pool, default_pool, 0,
pid_hash, pid_hash_cmp);
}
void service_pids_deinit(void)
{
struct hash_iterate_context *iter;
void *key, *value;
/* free all child process information */
iter = hash_table_iterate_init(service_pids);
while (hash_table_iterate(iter, &key, &value))
service_process_destroy(value);
hash_table_iterate_deinit(&iter);
hash_table_destroy(&service_pids);
}