diff options
Diffstat (limited to 'src/ipc/client.c')
-rw-r--r-- | src/ipc/client.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/src/ipc/client.c b/src/ipc/client.c new file mode 100644 index 0000000..483452e --- /dev/null +++ b/src/ipc/client.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "llist.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "master-service.h" +#include "ipc-group.h" +#include "ipc-connection.h" +#include "client.h" + +#include <unistd.h> + +struct client { + struct client *prev, *next; + + int fd; + struct io *io; + struct istream *input; + struct ostream *output; +}; + +static struct client *clients; + +static void client_input(struct client *client); + +static void +client_cmd_input(enum ipc_cmd_status status, const char *line, void *context) +{ + struct client *client = context; + char chr = '\0'; + + switch (status) { + case IPC_CMD_STATUS_REPLY: + chr = ':'; + break; + case IPC_CMD_STATUS_OK: + chr = '+'; + break; + case IPC_CMD_STATUS_ERROR: + chr = '-'; + break; + } + + T_BEGIN { + o_stream_nsend_str(client->output, + t_strdup_printf("%c%s\n", chr, line)); + } T_END; + + if (status != IPC_CMD_STATUS_REPLY && client->io == NULL) { + client->io = io_add(client->fd, IO_READ, client_input, client); + client_input(client); + } +} + +static void client_input(struct client *client) +{ + struct ipc_group *group; + struct ipc_connection *conn; + char *line, *id, *data; + unsigned int id_num; + bool ret; + + while ((line = i_stream_read_next_line(client->input)) != NULL) { + /* <ipc name> *|<id> <command> */ + id = strchr(line, '\t'); + if (id == NULL) + data = NULL; + else { + *id++ = '\0'; + data = strchr(id, '\t'); + } + if (data == NULL || data[1] == '\0') { + o_stream_nsend_str(client->output, "-Invalid input\n"); + continue; + } + *data++ = '\0'; + + group = ipc_group_lookup_name(line); + + ret = FALSE; + if (strcmp(id, "*") == 0) { + /* send to everyone */ + if (group == NULL) { + client_cmd_input(IPC_CMD_STATUS_OK, "", client); + } else { + ret = ipc_group_cmd(group, data, + client_cmd_input, client); + } + } else if (str_to_uint(id, &id_num) < 0) { + o_stream_nsend_str(client->output, + t_strdup_printf("-Invalid IPC connection id: %s\n", id)); + continue; + } else if (group == NULL) { + o_stream_nsend_str(client->output, + t_strdup_printf("-Unknown IPC group: %s\n", line)); + } else if ((conn = ipc_connection_lookup_id(group, id_num)) == NULL) { + o_stream_nsend_str(client->output, + t_strdup_printf("-Unknown IPC connection id: %u\n", id_num)); + continue; + } else { + ipc_connection_cmd(conn, data, client_cmd_input, client); + ret = TRUE; + } + + if (ret) { + /* we'll handle commands one at a time. stop reading + input until this command is finished. */ + io_remove(&client->io); + break; + } + } + if (client->input->eof || client->input->stream_errno != 0) + client_destroy(&client); +} + +struct client *client_create(int fd) +{ + struct client *client; + + client = i_new(struct client, 1); + client->fd = fd; + client->io = io_add(fd, IO_READ, client_input, client); + client->input = i_stream_create_fd(fd, SIZE_MAX); + client->output = o_stream_create_fd(fd, SIZE_MAX); + o_stream_set_no_error_handling(client->output, TRUE); + + DLLIST_PREPEND(&clients, client); + return client; +} + +void client_destroy(struct client **_client) +{ + struct client *client = *_client; + + *_client = NULL; + + DLLIST_REMOVE(&clients, client); + io_remove(&client->io); + i_stream_destroy(&client->input); + o_stream_destroy(&client->output); + if (close(client->fd) < 0) + i_error("close(client) failed: %m"); + i_free(client); + + master_service_client_connection_destroyed(master_service); +} + +void clients_destroy_all(void) +{ + while (clients != NULL) { + struct client *client = clients; + + client_destroy(&client); + } +} |