client.c revision e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (c) 2011-2012 Dovecot authors, see the included COPYING file */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "lib.h"
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#include "llist.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "ioloop.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "istream.h"
9bb91f1dbf7cf8cfbd2df7784101df98d59fb46dTimo Sirainen#include "ostream.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "master-service.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "ipc-group.h"
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include "ipc-connection.h"
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen#include "client.h"
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde#include <unistd.h>
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestruct client {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde struct client *prev, *next;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen int fd;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct io *io;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct istream *input;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct ostream *output;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen};
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic struct client *clients;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenstatic void client_input(struct client *client);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenclient_cmd_input(enum ipc_cmd_status status, const char *line, void *context)
c5a6a6565be93224fc26522eda855b0990f256e8Timo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct client *client = context;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde char chr = '\0';
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde switch (status) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde case IPC_CMD_STATUS_REPLY:
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde chr = ':';
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen break;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen case IPC_CMD_STATUS_OK:
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde chr = '+';
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen break;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen case IPC_CMD_STATUS_ERROR:
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen chr = '-';
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen break;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen T_BEGIN {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde o_stream_nsend_str(client->output,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen t_strdup_printf("%c%s\n", chr, line));
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde } T_END;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen if (status != IPC_CMD_STATUS_REPLY && client->io == NULL) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen client_input(client);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen }
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen}
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic void client_input(struct client *client)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen{
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen struct ipc_group *group;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen struct ipc_connection *conn;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen char *line, *id, *data;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen unsigned int id_num;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen bool ret;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen while ((line = i_stream_read_next_line(client->input)) != NULL) {
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* <ipc name> *|<id> <command> */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen id = strchr(line, '\t');
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen if (id == NULL)
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen data = NULL;
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen else {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde *id++ = '\0';
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen data = strchr(id, '\t');
c5a6a6565be93224fc26522eda855b0990f256e8Timo Sirainen }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (data == NULL || data[1] == '\0') {
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen o_stream_nsend_str(client->output, "-Invalid input\n");
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde continue;
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen *data++ = '\0';
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
c5a6a6565be93224fc26522eda855b0990f256e8Timo Sirainen group = ipc_group_lookup_name(line);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde ret = FALSE;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (strcmp(id, "*") == 0) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* send to everyone */
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen if (group == NULL) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde client_cmd_input(IPC_CMD_STATUS_OK, "", client);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde } else {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde ret = ipc_group_cmd(group, data,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen client_cmd_input, client);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } else if (str_to_uint(id, &id_num) < 0) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen o_stream_nsend_str(client->output,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen t_strdup_printf("-Invalid IPC connection id: %s\n", id));
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde continue;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen } else if (group == NULL) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen o_stream_nsend_str(client->output,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde t_strdup_printf("-Unknown IPC group: %s\n", line));
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde } else if ((conn = ipc_connection_lookup_id(group, id_num)) == NULL) {
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen o_stream_nsend_str(client->output,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde t_strdup_printf("-Unknown IPC connection id: %u\n", id_num));
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen continue;
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen } else {
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen ipc_connection_cmd(conn, data, client_cmd_input, client);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde ret = TRUE;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (ret) {
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* we'll handle commands one at a time. stop reading
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen input until this command is finished. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen io_remove(&client->io);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen break;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen }
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen if (client->input->eof || client->input->stream_errno != 0)
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen client_destroy(&client);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstruct client *client_create(int fd)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct client *client;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen client = i_new(struct client, 1);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen client->fd = fd;
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen client->input = i_stream_create_fd(fd, (size_t)-1, FALSE);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen DLLIST_PREPEND(&clients, client);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen return client;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen}
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenvoid client_destroy(struct client **_client)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen{
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen struct client *client = *_client;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde *_client = NULL;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen DLLIST_REMOVE(&clients, client);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen if (client->io != NULL)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen io_remove(&client->io);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen i_stream_destroy(&client->input);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen o_stream_destroy(&client->output);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (close(client->fd) < 0)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen i_error("close(client) failed: %m");
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen i_free(client);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen master_service_client_connection_destroyed(master_service);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen}
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenvoid clients_destroy_all(void)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen{
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen while (clients != NULL) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen struct client *client = clients;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen client_destroy(&client);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen }
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen}
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen