master-service.c revision e6b4168ba670d9e51ea7877661def039ae6b53c3
/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "lib-signals.h"
#include "ioloop.h"
#include "array.h"
#include "env-util.h"
#include "home-expand.h"
#include "process-title.h"
#include "restrict-access.h"
#include "fd-close-on-exec.h"
#include "settings-parser.h"
#include "syslog-util.h"
#include "master-service-private.h"
#include "master-service-settings.h"
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
/* when we're full of connections, how often to check if login state has
changed. we normally notice it immediately because of a signal, so this is
just a fallback against race conditions. */
#define MASTER_SERVICE_STATE_CHECK_MSECS 1000
struct master_service *master_service;
const char *master_service_getopt_string(void)
{
return "c:ko:Os:L";
}
{
/* warn about being killed because of some signal, except SIGINT (^C)
which is too common at least while testing :) */
i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
}
}
static void
{
}
{
i_fatal("Dovecot version mismatch: "
"(if you don't care, set version_ignore=yes)",
}
}
struct master_service *
{
extern char **environ;
struct master_service *service;
const char *str;
#ifdef DEBUG
(flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
int count;
}
#endif
/* NOTE: we start rooted, so keep the code minimal until
restrict_access_by_env() is called */
lib_init();
/* Set a logging prefix temporarily. This will be ignored once the log
is properly initialized */
/* keep getopt_str first in case it contains "+" */
if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
} else {
}
/* set up some kind of logging until we know exactly how and where
we want to log */
} else {
}
return service;
}
{
int c;
service->getopt_str)) > 0) {
break;
}
return c;
}
const char *prefix)
{
const char *path;
return;
}
/* logging via log service */
return;
}
return;
}
/* log to syslog */
int facility;
&facility))
} else {
/* log to file or stderr */
}
if (*path != '\0')
if (*path != '\0')
}
bool set)
{
}
{
int i;
switch (opt) {
case 'c':
break;
case 'k':
break;
case 'o':
break;
case 'O':
break;
case 's':
service->socket_count = i;
break;
case 'L':
break;
default:
return FALSE;
}
return TRUE;
}
{
else
}
static void master_status_error(void *context)
{
/* status fd is a write-only pipe, so if we're here it means the
master wants us to die (or died itself). don't die until all
service connections are finished. */
/* the log fd may also be closed already, don't die when trying to
log later */
}
{
const char *value;
unsigned int count;
/* set default signal handlers */
}
i_fatal("Must be started by dovecot master process");
/* initialize master_status structure */
/* set the default limit */
if (count == 0)
/* set the default service count */
if (count > 0)
/* start listening errors for status fd, it means master died */
} else {
}
/* we already have a connection to be served */
}
}
void master_service_env_clean(bool preserve_home)
{
#ifdef DEBUG
#endif
/* Note that if the original environment was set with env_put(), the
environment strings will be invalid after env_clean(). That's why
we t_strconcat() them above. */
env_clean();
#ifdef DEBUG
#endif
}
unsigned int client_limit)
{
unsigned int used;
}
{
return service->total_available_count;
}
unsigned int count)
{
unsigned int used;
}
}
{
return service->service_count_left;
}
{
return service->socket_count;
}
void (*callback)(void))
{
}
{
return service->config_path;
}
{
return service->version_string;
}
{
}
{
}
{
}
{
return;
if (ret < 0) {
/* anvil process was probably recreated, don't bother
logging an error about losing connection to it */
return;
}
i_error("write(anvil) failed: %m");
} else if (ret == 0)
i_error("write(anvil) failed: EOF");
else {
}
}
{
/* we can listen again */
} else {
/* we have only limited amount of service requests left */
if (service->service_count_left == 0) {
}
}
/* we've finished handling all clients, and
a) master has closed the connection
b) there are no listeners (std-client?) */
}
}
enum master_login_state state)
{
switch (state) {
return;
/* some processes should now be able to handle new connections,
although we can't. but there may be race conditions, so
make sure that we'll check again soon if the state has
changed to "full" without our knowledge. */
service);
return;
case MASTER_LOGIN_STATE_FULL:
/* make sure we're listening for more connections */
return;
}
}
{
int ret;
if (ret < 0)
i_error("lseek(login notify fd) failed: %m");
else
}
{
i_error("close(master config fd) failed: %m");
}
}
{
}
lib_deinit();
}
static void master_service_listen(struct master_service_listener *l)
{
struct master_service_connection conn;
/* we are full. stop listening for now, unless overflow
callback destroys one of the existing connections */
if (service->call_avail_overflow &&
}
return;
}
}
return;
i_error("net_accept() failed: %m");
return;
}
/* it's not a socket. probably a fifo. use the "listener"
as the connection fd and stop the listener. */
l->fd = -1;
}
/* we're dying as soon as this connection closes. */
}
}
{
unsigned int i;
if (service->socket_count == 0)
return;
for (i = 0; i < service->socket_count; i++) {
l->fd = MASTER_LISTEN_FD_FIRST + i;
}
}
{
unsigned int i;
for (i = 0; i < service->socket_count; i++) {
}
}
}
{
unsigned int i;
for (i = 0; i < service->socket_count; i++) {
}
}
}
{
return TRUE;
if (!service->initial_status_sent)
return TRUE;
return FALSE;
}
{
return; /* closed */
sizeof(service->master_status));
if (ret > 0) {
/* success */
/* delayed important update sent successfully */
}
} else if (ret == 0) {
/* shouldn't happen? */
i_error("write(master_status_fd) returned 0");
/* failure */
i_error("write(master_status_fd) failed: %m");
} else if (master_status_update_is_important(service)) {
/* reader is busy, but it's important to get this notification
through. send it when possible. */
}
}
}