DEF(SET_BOOL, drop_priv_before_exec),
+ DEF(SET_UINT, process_min_avail),
DEF(SET_UINT, process_limit),
DEF(SET_UINT, client_limit),
DEF(SET_UINT, service_count),
MEMBER(drop_priv_before_exec) FALSE,
+ MEMBER(process_min_avail) 0,
MEMBER(process_limit) (unsigned int)-1,
MEMBER(client_limit) 0,
MEMBER(service_count) 0,
}
services = array_get(&set->services, &count);
for (i = 0; i < count; i++) {
- if (*services[i]->name == '\0') {
+ struct service_settings *service = services[i];
+
+ if (*service->name == '\0') {
*error_r = t_strdup_printf(
"Service #%d is missing name", i);
return FALSE;
}
- if (*services[i]->type != '\0' &&
- strcmp(services[i]->type, "log") != 0 &&
- strcmp(services[i]->type, "config") != 0 &&
- strcmp(services[i]->type, "anvil") != 0 &&
- strcmp(services[i]->type, "auth") != 0 &&
- strcmp(services[i]->type, "auth-source") != 0) {
+ if (*service->type != '\0' &&
+ strcmp(service->type, "log") != 0 &&
+ strcmp(service->type, "config") != 0 &&
+ strcmp(service->type, "anvil") != 0 &&
+ strcmp(service->type, "auth") != 0 &&
+ strcmp(service->type, "auth-source") != 0) {
*error_r = t_strconcat("Unknown service type: ",
- services[i]->type, NULL);
+ service->type, NULL);
return FALSE;
}
for (j = 0; j < i; j++) {
- if (strcmp(services[i]->name, services[j]->name) == 0) {
+ if (strcmp(service->name, services[j]->name) == 0) {
*error_r = t_strdup_printf(
"Duplicate service name: %s",
- services[i]->name);
+ service->name);
return FALSE;
}
}
}
for (i = 0; i < count; i++) {
- if (*services[i]->executable != '/') {
- services[i]->executable =
+ struct service_settings *service = services[i];
+
+ if (*service->executable != '/') {
+ service->executable =
p_strconcat(pool, set->libexec_dir, "/",
- services[i]->executable, NULL);
+ service->executable, NULL);
}
- if (*services[i]->chroot != '/' &&
- *services[i]->chroot != '\0') {
- services[i]->chroot =
+ if (*service->chroot != '/' && *service->chroot != '\0') {
+ service->chroot =
p_strconcat(pool, set->base_dir, "/",
- services[i]->chroot, NULL);
+ service->chroot, NULL);
}
- if (services[i]->drop_priv_before_exec &&
- *services[i]->chroot != '\0') {
+ if (service->drop_priv_before_exec &&
+ *service->chroot != '\0') {
*error_r = t_strdup_printf("service(%s): "
"drop_priv_before_exec=yes can't be "
- "used with chroot", services[i]->name);
+ "used with chroot", service->name);
+ return FALSE;
+ }
+ if (service->process_min_avail > service->process_limit) {
+ *error_r = t_strdup_printf("service(%s): "
+ "process_min_avail is higher than process_limit",
+ service->name);
return FALSE;
}
- fix_file_listener_paths(&services[i]->unix_listeners,
+ fix_file_listener_paths(&service->unix_listeners,
pool, set->base_dir);
- fix_file_listener_paths(&services[i]->fifo_listeners,
+ fix_file_listener_paths(&service->fifo_listeners,
pool, set->base_dir);
}
set->protocols_split = p_strsplit(pool, set->protocols, " ");
#define SERVICE_STARTUP_FAILURE_THROTTLE_SECS 60
-void service_monitor_stop(struct service *service);
+static void service_monitor_start_extra_avail(struct service *service);
static void service_status_input(struct service *service)
{
process->available_count - status.available_count;
if (status.available_count == 0) {
i_assert(service->process_avail > 0);
- if (--service->process_avail == 0)
- service_monitor_listen_start(service);
+ service->process_avail--;
+
+ service_monitor_start_extra_avail(service);
+ service_monitor_listen_start(service);
}
process->idle_start = 0;
} else {
service_monitor_listen_stop(service);
}
+static void service_monitor_start_extra_avail(struct service *service)
+{
+ unsigned int i, count;
+
+ if (service->process_avail >= service->set->process_min_avail)
+ return;
+
+ count = service->set->process_min_avail - service->process_avail;
+ if (service->process_count + count > service->process_limit)
+ count = service->process_limit - service->process_count;
+
+ for (i = 0; i < count; i++) {
+ if (service_process_create(service, NULL, NULL) == NULL) {
+ service_monitor_throttle(service);
+ break;
+ }
+ }
+ if (i > 0 && service->listening) {
+ /* we created some processes, they'll do the listening now */
+ service_monitor_listen_stop(service);
+ }
+}
+
void service_monitor_listen_start(struct service *service)
{
struct service_listener *const *listeners;
unsigned int i, count;
+ if (service->process_avail > 0)
+ return;
+
service->listening = TRUE;
service->listen_pending = FALSE;
service_status_input, services[i]);
}
- if (services[i]->status_fd[0] != -1)
+ if (services[i]->status_fd[0] != -1) {
+ service_monitor_start_extra_avail(services[i]);
service_monitor_listen_start(services[i]);
+ }
}
if (service_process_create(service_list->log, NULL, NULL) != NULL)
service_process_failure(process, status);
}
service_process_destroy(process);
+ service_monitor_start_extra_avail(service);
- if (service->process_avail == 0 && service->to_throttle == NULL)
+ if (service->to_throttle == NULL)
service_monitor_listen_start(service);
}
}