master.c revision 45312f52ff3a3d4c137447be4c7556500c2f8bf2
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "common.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "hash.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "buffer.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "ioloop.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "network.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "fdpass.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "istream.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "env-util.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "write-full.h"
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen#include "master.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "client-common.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen#include <unistd.h>
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic int master_fd;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic struct io *io_master;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic struct hash_table *master_requests;
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainenstatic unsigned int master_tag_counter;
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainenstatic unsigned int master_pos;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic char master_buf[sizeof(struct master_login_reply)];
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic struct client destroyed_client;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic void client_call_master_callback(struct client *client,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen const struct master_login_reply *reply)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen{
cd466fe7b84b0223735a6469c7f7bc225f65996dTimo Sirainen master_callback_t *master_callback;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen master_callback = client->master_callback;
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen client->master_tag = 0;
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen client->master_callback = NULL;
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen master_callback(client, reply);
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen}
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d56384d5226c8860079d0d0b08b83404e8c42986Timo Sirainenstatic void request_handle(struct master_login_reply *reply)
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen{
d56384d5226c8860079d0d0b08b83404e8c42986Timo Sirainen struct client *client;
d56384d5226c8860079d0d0b08b83404e8c42986Timo Sirainen
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen if (reply->tag == 0 && !process_per_connection) {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen /* this means we have to start listening again.
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen we've reached maximum number of login processes. */
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen main_listen_start();
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen return;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen }
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen client = hash_table_lookup(master_requests, POINTER_CAST(reply->tag));
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen if (client == NULL)
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen i_fatal("Master sent reply with unknown tag %u", reply->tag);
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen hash_table_remove(master_requests, POINTER_CAST(reply->tag));
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (client != &destroyed_client) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen client_call_master_callback(client, reply);
6a9f9a5101b665fd2ef80c9e048a5eace78e01efTimo Sirainen /* NOTE: client may be destroyed now */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen}
6a9f9a5101b665fd2ef80c9e048a5eace78e01efTimo Sirainen
6a9f9a5101b665fd2ef80c9e048a5eace78e01efTimo Sirainenvoid master_request_login(struct client *client, master_callback_t *callback,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen unsigned int auth_pid, unsigned int auth_id)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen{
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen buffer_t *buf;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen struct master_login_request *req;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen struct stat st;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen const unsigned char *data;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen size_t size;
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen ssize_t ret;
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen unsigned int cmd_tag_size;
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen i_assert(auth_pid != 0);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (master_fd == -1) {
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen struct master_login_reply reply;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_assert(closing_down);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen memset(&reply, 0, sizeof(reply));
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen callback(client, &reply);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen return;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen }
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen data = i_stream_get_data(client->input, &size);
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen cmd_tag_size = client->auth_command_tag == NULL ? 0 :
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen strlen(client->auth_command_tag);
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(),
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen sizeof(*req) + size + cmd_tag_size);
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen buffer_write(buf, sizeof(*req), client->auth_command_tag, cmd_tag_size);
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen buffer_write(buf, sizeof(*req) + cmd_tag_size, data, size);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen req = buffer_get_space_unsafe(buf, 0, sizeof(*req));
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->version = MASTER_LOGIN_PROTOCOL_VERSION;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->tag = ++master_tag_counter;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (req->tag == 0)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->tag = ++master_tag_counter;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen req->auth_pid = auth_pid;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->auth_id = auth_id;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->local_ip = client->local_ip;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->remote_ip = client->ip;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->cmd_tag_size = cmd_tag_size;
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen req->data_size = req->cmd_tag_size + size;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#if (LOGIN_MAX_INBUF_SIZE*2) != MASTER_LOGIN_MAX_DATA_SIZE
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen# error buffer max sizes unsynced
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#endif
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_assert(req->data_size <= LOGIN_MAX_INBUF_SIZE);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
a29a5b7520f7b8d6cdaf97e66d184b6a9e4f4ecfTimo Sirainen if (fstat(client->fd, &st) < 0)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_fatal("fstat(client) failed: %m");
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen req->ino = st.st_ino;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen ret = fd_send(master_fd, client->fd, buf->data, buf->used);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (ret < 0)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_fatal("fd_send(%d) failed: %m", client->fd);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if ((size_t)ret != buf->used) {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_fatal("fd_send() sent only %d of %d bytes",
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen (int)ret, (int)buf->used);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen }
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen client->master_tag = req->tag;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen client->master_callback = callback;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen hash_table_insert(master_requests, POINTER_CAST(req->tag), client);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen}
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainenvoid master_request_abort(struct client *client)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen{
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen struct master_login_reply reply;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen /* we're still going to get the reply from the master, so just
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen remember that we want to ignore it */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen hash_table_update(master_requests, POINTER_CAST(client->master_tag),
af208f4a43a81a39a2ec44e68d65d12b95f1b386Timo Sirainen &destroyed_client);
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen memset(&reply, 0, sizeof(reply));
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen client_call_master_callback(client, &reply);
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen}
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainenvoid master_notify_state_change(enum master_login_state state)
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen{
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen struct master_login_request req;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen if (io_master == NULL)
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen return;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen memset(&req, 0, sizeof(req));
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen req.version = MASTER_LOGIN_PROTOCOL_VERSION;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen req.tag = state;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen req.ino = (ino_t)-1;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen /* sending -1 as fd does the notification */
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen if (fd_send(master_fd, -1, &req, sizeof(req)) != sizeof(req))
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen i_fatal("fd_send(-1) failed: %m");
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen}
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainenvoid master_close(void)
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen{
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen if (io_master == NULL)
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen return;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen io_remove(&io_master);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (close(master_fd) < 0)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_fatal("close(master) failed: %m");
9453e8d75cfd8fab2232cf772e9b120f308fb3eeTimo Sirainen master_fd = -1;
9453e8d75cfd8fab2232cf772e9b120f308fb3eeTimo Sirainen
9453e8d75cfd8fab2232cf772e9b120f308fb3eeTimo Sirainen closing_down = TRUE;
9453e8d75cfd8fab2232cf772e9b120f308fb3eeTimo Sirainen main_listen_stop();
9453e8d75cfd8fab2232cf772e9b120f308fb3eeTimo Sirainen main_unref();
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen /* may call this function again through main_unref() */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen clients_destroy_all();
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen}
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic void master_exec(int fd)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen{
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen static char dovecot[] = "dovecot";
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen char *argv[] = { dovecot, NULL };
switch (fork()) {
case -1:
i_fatal("fork() failed: %m");
case 0:
if (dup2(fd, 0) < 0)
i_fatal("master_exec: dup2(%d, 0) failed: %m", fd);
(void)close(fd);
if (setsid() < 0)
i_fatal("setsid() failed: %m");
env_put("DOVECOT_INETD=1");
execv(SBINDIR"/dovecot", argv);
i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
SBINDIR"/dovecot");
default:
(void)close(fd);
}
}
static void master_read_env(int fd)
{
struct istream *input;
const char *line;
env_clean();
/* read environment variable lines until empty line comes */
input = i_stream_create_fd(fd, 8192, FALSE);
do {
switch (i_stream_read(input)) {
case -1:
i_fatal("EOF while reading environment from master");
case -2:
i_fatal("Too large environment line from master");
}
while ((line = i_stream_next_line(input)) != NULL &&
*line != '\0')
env_put(line);
} while (line == NULL);
i_stream_destroy(&input);
}
int master_connect(const char *group_name)
{
const char *path = PKG_RUNDIR"/master";
int i, fd = -1;
for (i = 0; i < 5 && fd == -1; i++) {
fd = net_connect_unix(path);
if (fd != -1)
break;
if (errno == ECONNREFUSED) {
if (unlink(path) < 0)
i_error("unlink(%s) failed: %m", path);
} else if (errno != ENOENT) {
i_fatal("Can't connect to master UNIX socket %s: %m",
path);
}
/* need to create it */
fd = net_listen_unix(path, 16);
if (fd != -1) {
master_exec(fd);
fd = -1;
} else if (errno != EADDRINUSE) {
i_fatal("Can't create master UNIX socket %s: %m", path);
}
}
if (fd == -1)
i_fatal("Couldn't use/create UNIX socket %s", path);
if (group_name[0] == '\0')
i_fatal("No login group name set");
if (strlen(group_name) >= 256)
i_fatal("Login group name too large: %s", group_name);
/* group_name length is now guaranteed to be in range of 1..255 so we
can send <length byte><name> */
group_name = t_strdup_printf("%c%s", (unsigned char)strlen(group_name),
group_name);
if (write_full(fd, group_name, strlen(group_name)) < 0)
i_fatal("write_full(master_fd) failed: %m");
master_read_env(fd);
return fd;
}
static void master_input(void *context ATTR_UNUSED)
{
int ret;
ret = net_receive(master_fd, master_buf + master_pos,
sizeof(master_buf) - master_pos);
if (ret < 0) {
/* master died, kill all clients logging in */
master_close();
return;
}
master_pos += ret;
if (master_pos < sizeof(master_buf))
return;
/* reply is now read */
request_handle((void *)master_buf);
master_pos = 0;
}
void master_init(int fd)
{
main_ref();
master_fd = fd;
master_requests = hash_table_create(system_pool, system_pool,
0, NULL, NULL);
master_pos = 0;
io_master = io_add(master_fd, IO_READ, master_input, NULL);
}
void master_deinit(void)
{
hash_table_destroy(&master_requests);
if (io_master != NULL)
io_remove(&io_master);
}