service.c revision b203ee2724dbe50332502a6d9521a7cdae38380f
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "common.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "ioloop.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "array.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "aqueue.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "hash.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "str.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "master-service.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "master-service-settings.h"
0662ed52e814f8f08ef0e09956413a792584eddffuankg#include "service.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "service-anvil.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "service-process.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include "service-monitor.h"
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include <unistd.h>
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes#include <signal.h>
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
70953fb44a7140fe206c3a5f011e24209c8c5c6abnicholes#define SERVICE_DIE_TIMEOUT_MSECS (1000*60)
70953fb44a7140fe206c3a5f011e24209c8c5c6abnicholes#define SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS 2
16b55a35cff91315d261d1baa776138af465c4e4fuankg
16b55a35cff91315d261d1baa776138af465c4e4fuankgHASH_TABLE_TYPE(pid_process) service_pids;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesvoid service_error(struct service *service, const char *format, ...)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes va_list args;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes va_start(args, format);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes i_error("service(%s): %s", service->set->name,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes t_strdup_vprintf(format, args));
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes va_end(args);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes}
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesstatic struct service_listener *
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesservice_create_file_listener(struct service *service,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes enum service_listener_type type,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const struct file_listener_settings *set,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char **error_r)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct service_listener *l;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char *set_name;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes gid_t gid;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l = p_new(service->list->pool, struct service_listener, 1);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->service = service;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->type = type;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->fd = -1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->set.fileset.set = set;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->name = strrchr(set->path, '/');
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (l->name != NULL)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->name++;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes else
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->name = set->path;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (get_uidgid(set->user, &l->set.fileset.uid, &gid, error_r) < 0)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes set_name = "user";
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes else if (get_gid(set->group, &l->set.fileset.gid, error_r) < 0)
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg set_name = "group";
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes else
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return l;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *error_r = t_strdup_printf(
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes "%s (See service %s { %s_listener %s { %s } } setting)",
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *error_r, service->set->name,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes type == SERVICE_LISTENER_UNIX ? "unix" : "fifo",
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes set->path, set_name);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return NULL;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes}
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesstatic int
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesresolve_ip(const char *address, const struct ip_addr **ips_r,
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg unsigned int *ips_count_r, const char **error_r)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct ip_addr *ip_list;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes unsigned int ips_count;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes int ret;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (address == NULL || strcmp(address, "*") == 0) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* IPv4 any */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes ip_list = t_new(struct ip_addr, 1);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes net_get_ip_any4(ip_list);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *ips_r = ip_list;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *ips_count_r = 1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return 0;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (strcmp(address, "::") == 0 || strcmp(address, "[::]") == 0) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* IPv6 any */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes ip_list = t_new(struct ip_addr, 1);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes net_get_ip_any6(ip_list);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *ips_r = ip_list;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *ips_count_r = 1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return 0;
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* Return the first IP if there happens to be multiple. */
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg ret = net_gethostbyname(address, &ip_list, &ips_count);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (ret != 0) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *error_r = t_strdup_printf("Can't resolve address %s: %s",
0a39e7683f6611d66c55712f50bb240428d832a1bnicholes address, net_gethosterror(ret));
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return -1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (ips_count < 1) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *error_r = t_strdup_printf("No IPs for address: %s", address);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return -1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg *ips_r = ip_list;
0662ed52e814f8f08ef0e09956413a792584eddffuankg *ips_count_r = ips_count;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return 0;
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg}
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesstatic struct service_listener *
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesservice_create_one_inet_listener(struct service *service,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const struct inet_listener_settings *set,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char *address, const struct ip_addr *ip,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char **error_r)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct service_listener *l;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes i_assert(set->port != 0);
0662ed52e814f8f08ef0e09956413a792584eddffuankg
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l = p_new(service->list->pool, struct service_listener, 1);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->service = service;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->type = SERVICE_LISTENER_INET;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->fd = -1;
0662ed52e814f8f08ef0e09956413a792584eddffuankg l->set.inetset.set = set;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->set.inetset.ip = *ip;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->inet_address = p_strdup(service->list->pool, address);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l->name = set->name;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (set->port > 65535) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *error_r = t_strdup_printf("Invalid port: %u", set->port);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return NULL;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
0662ed52e814f8f08ef0e09956413a792584eddffuankg return l;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes}
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
ac7985784d08a3655291f24f711812b4d8b1cbcffuankgstatic int
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesservice_create_inet_listeners(struct service *service,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const struct inet_listener_settings *set,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char **error_r)
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes static struct service_listener *l;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char *const *tmp, *addresses;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const struct ip_addr *ips;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes unsigned int i, ips_count;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes bool ssl_disabled = strcmp(service->set->master_set->ssl, "no") == 0;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (set->port == 0) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* disabled */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return 0;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (*set->address != '\0')
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes addresses = set->address;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes else {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* use the default listen address */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes addresses = service->set->master_set->listen;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes tmp = t_strsplit_spaces(addresses, ", ");
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes for (; *tmp != NULL; tmp++) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char *address = *tmp;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (set->ssl && ssl_disabled)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes continue;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (resolve_ip(address, &ips, &ips_count, error_r) < 0)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return -1;
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes for (i = 0; i < ips_count; i++) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes l = service_create_one_inet_listener(service, set,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes address, &ips[i],
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes error_r);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (l == NULL)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return -1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes array_append(&service->listeners, &l, 1);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->have_inet_listeners = TRUE;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return 0;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes}
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesstatic int service_get_groups(const char *groups, pool_t pool,
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char **gids_r, const char **error_r)
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes const char *const *tmp;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes string_t *str;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes gid_t gid;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes str = t_str_new(64);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes for (tmp = t_strsplit(groups, ","); *tmp != NULL; tmp++) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (get_gid(*tmp, &gid, error_r) < 0)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return -1;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (str_len(str) > 0)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes str_append_c(str, ',');
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes str_append(str, dec2str(gid));
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes *gids_r = p_strdup(pool, str_c(str));
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes return 0;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes}
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholesstatic struct service *
ac7985784d08a3655291f24f711812b4d8b1cbcffuankgservice_create(pool_t pool, const struct service_settings *set,
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg struct service_list *service_list, const char **error_r)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes{
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct file_listener_settings *const *unix_listeners;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct file_listener_settings *const *fifo_listeners;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct inet_listener_settings *const *inet_listeners;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes struct service *service;
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg struct service_listener *l;
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg unsigned int i, unix_count, fifo_count, inet_count;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service = p_new(pool, struct service, 1);
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->list = service_list;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->set = set;
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg service->throttle_secs = SERVICE_STARTUP_FAILURE_THROTTLE_MIN_SECS;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->client_limit = set->client_limit != 0 ? set->client_limit :
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes set->master_set->default_client_limit;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (set->service_count > 0 &&
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->client_limit > set->service_count)
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->client_limit = set->service_count;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->vsz_limit = set->vsz_limit != (uoff_t)-1 ? set->vsz_limit :
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes set->master_set->default_vsz_limit;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->idle_kill = set->idle_kill != 0 ? set->idle_kill :
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg set->master_set->default_idle_kill;
0662ed52e814f8f08ef0e09956413a792584eddffuankg service->type = service->set->parsed_type;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->executable = set->executable;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (set->process_limit == 0) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* use default */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->process_limit =
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes set->master_set->default_process_limit;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes } else {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes service->process_limit = set->process_limit;
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes }
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes /* default gid to user's primary group */
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes if (get_uidgid(set->user, &service->uid, &service->gid, error_r) < 0) {
0662ed52e814f8f08ef0e09956413a792584eddffuankg switch (set->user_default) {
bb2b38cd44b032118359afbc743efbea12f48e61bnicholes case SERVICE_USER_DEFAULT_NONE:
ac7985784d08a3655291f24f711812b4d8b1cbcffuankg *error_r = t_strdup_printf(
"%s (See service %s { user } setting)",
*error_r, set->name);
break;
case SERVICE_USER_DEFAULT_INTERNAL:
*error_r = t_strconcat(*error_r,
" (See default_internal_user setting)", NULL);
break;
case SERVICE_USER_DEFAULT_LOGIN:
*error_r = t_strconcat(*error_r,
" (See default_login_user setting)", NULL);
break;
}
return NULL;
}
if (*set->group != '\0') {
if (get_gid(set->group, &service->gid, error_r) < 0) {
*error_r = t_strdup_printf(
"%s (See service %s { group } setting)",
*error_r, set->name);
return NULL;
}
}
if (get_gid(set->privileged_group, &service->privileged_gid,
error_r) < 0) {
*error_r = t_strdup_printf(
"%s (See service %s { privileged_group } setting)",
*error_r, set->name);
return NULL;
}
if (*set->extra_groups != '\0') {
if (service_get_groups(set->extra_groups, pool,
&service->extra_gids, error_r) < 0) {
*error_r = t_strdup_printf(
"%s (See service %s { extra_groups } setting)",
*error_r, set->name);
return NULL;
}
}
/* set these later, so if something fails we don't have to worry about
closing them */
service->log_fd[0] = -1;
service->log_fd[1] = -1;
service->status_fd[0] = -1;
service->status_fd[1] = -1;
service->log_process_internal_fd = -1;
service->login_notify_fd = -1;
if (service->type == SERVICE_TYPE_ANVIL) {
service->status_fd[0] = service_anvil_global->status_fd[0];
service->status_fd[1] = service_anvil_global->status_fd[1];
}
if (array_is_created(&set->unix_listeners))
unix_listeners = array_get(&set->unix_listeners, &unix_count);
else {
unix_listeners = NULL;
unix_count = 0;
}
if (array_is_created(&set->fifo_listeners))
fifo_listeners = array_get(&set->fifo_listeners, &fifo_count);
else {
fifo_listeners = NULL;
fifo_count = 0;
}
if (array_is_created(&set->inet_listeners))
inet_listeners = array_get(&set->inet_listeners, &inet_count);
else {
inet_listeners = NULL;
inet_count = 0;
}
if (unix_count == 0 && service->type == SERVICE_TYPE_CONFIG) {
*error_r = "Service must have unix listeners";
return NULL;
}
p_array_init(&service->listeners, pool,
unix_count + fifo_count + inet_count);
for (i = 0; i < unix_count; i++) {
if (unix_listeners[i]->mode == 0) {
/* disabled */
continue;
}
l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
unix_listeners[i], error_r);
if (l == NULL)
return NULL;
array_append(&service->listeners, &l, 1);
}
for (i = 0; i < fifo_count; i++) {
if (fifo_listeners[i]->mode == 0) {
/* disabled */
continue;
}
l = service_create_file_listener(service, SERVICE_LISTENER_FIFO,
fifo_listeners[i], error_r);
if (l == NULL)
return NULL;
array_append(&service->listeners, &l, 1);
}
for (i = 0; i < inet_count; i++) {
if (service_create_inet_listeners(service, inet_listeners[i],
error_r) < 0)
return NULL;
}
if (access(t_strcut(service->executable, ' '), X_OK) < 0) {
*error_r = t_strdup_printf("access(%s) failed: %m",
t_strcut(service->executable, ' '));
return NULL;
}
return service;
}
struct service *
service_lookup(struct service_list *service_list, const char *name)
{
struct service *const *services;
array_foreach(&service_list->services, services) {
struct service *service = *services;
if (strcmp(service->set->name, name) == 0)
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;
}
static int
services_create_real(const struct master_settings *set, pool_t pool,
struct service_list **services_r, const char **error_r)
{
struct service_list *service_list;
struct service *service;
struct service_settings *const *service_settings;
const char *error;
unsigned int i, count;
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->set = set;
service_list->master_log_fd[0] = -1;
service_list->master_log_fd[1] = -1;
service_list->master_dead_pipe_fd[0] = -1;
service_list->master_dead_pipe_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;
}
int services_create(const struct master_settings *set,
struct service_list **services_r, const char **error_r)
{
pool_t pool;
pool = pool_alloconly_create("services pool", 4096);
if (services_create_real(set, pool, services_r, error_r) < 0) {
pool_unref(&pool);
return -1;
}
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, bool wait)
{
/* make sure we log if child processes died unexpectedly */
service_list->destroying = TRUE;
services_monitor_reap_children();
services_monitor_stop(service_list, wait);
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);
}
static void service_drop_listener_connections(struct service *service)
{
struct service_listener *const *listenerp;
int fd;
array_foreach(&service->listeners, listenerp) {
switch ((*listenerp)->type) {
case SERVICE_LISTENER_UNIX:
case SERVICE_LISTENER_INET:
if ((*listenerp)->fd == -1) {
/* already stopped listening */
break;
}
while ((fd = net_accept((*listenerp)->fd,
NULL, NULL)) >= 0)
i_close_fd(&fd);
break;
case SERVICE_LISTENER_FIFO:
break;
}
}
}
void service_throttle(struct service *service, unsigned int secs)
{
if (service->to_throttle != NULL || service->list->destroyed)
return;
if (service->processes == NULL)
service_drop_listener_connections(service);
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)
{
hash_table_create_direct(&service_pids, default_pool, 0);
}
void service_pids_deinit(void)
{
struct hash_iterate_context *iter;
void *key;
struct service_process *process;
/* free all child process information */
iter = hash_table_iterate_init(service_pids);
while (hash_table_iterate(iter, service_pids, &key, &process))
service_process_destroy(process);
hash_table_iterate_deinit(&iter);
hash_table_destroy(&service_pids);
}