1 /* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "ipc-connection.h"
6 #include "ipc-group.h"
7 
8 struct ipc_group_cmd {
9 	ipc_cmd_callback_t *callback;
10 	void *context;
11 
12 	int refcount;
13 	char *first_error;
14 };
15 
16 static ARRAY(struct ipc_group *) ipc_groups;
17 
ipc_group_alloc(int listen_fd)18 struct ipc_group *ipc_group_alloc(int listen_fd)
19 {
20 	struct ipc_group *group;
21 
22 	i_assert(ipc_group_lookup_listen_fd(listen_fd) == NULL);
23 
24 	group = i_new(struct ipc_group, 1);
25 	group->listen_fd = listen_fd;
26 	array_push_back(&ipc_groups, &group);
27 	return group;
28 }
29 
ipc_group_free(struct ipc_group ** _group)30 void ipc_group_free(struct ipc_group **_group)
31 {
32 	struct ipc_group *const *groups, *group = *_group;
33 	unsigned int i, count;
34 
35 	i_assert(group->connections == NULL);
36 
37 	*_group = NULL;
38 	groups = array_get(&ipc_groups, &count);
39 	for (i = 0; i < count; i++) {
40 		if (groups[i] == group) {
41 			array_delete(&ipc_groups, i, 1);
42 			break;
43 		}
44 	}
45 	i_free(group->name);
46 	i_free(group);
47 }
48 
ipc_group_lookup_listen_fd(int listen_fd)49 struct ipc_group *ipc_group_lookup_listen_fd(int listen_fd)
50 {
51 	struct ipc_group *group;
52 
53 	array_foreach_elem(&ipc_groups, group) {
54 		if (group->listen_fd == listen_fd)
55 			return group;
56 	}
57 	return NULL;
58 }
59 
ipc_group_lookup_name(const char * name)60 struct ipc_group *ipc_group_lookup_name(const char *name)
61 {
62 	struct ipc_group *group;
63 
64 	array_foreach_elem(&ipc_groups, group) {
65 		if (group->name != NULL &&
66 		    strcmp(group->name, name) == 0)
67 			return group;
68 	}
69 	return NULL;
70 }
71 
ipc_group_update_name(struct ipc_group * group,const char * name)72 int ipc_group_update_name(struct ipc_group *group, const char *name)
73 {
74 	if (group->name == NULL)
75 		group->name = i_strdup(name);
76 	else if (strcmp(group->name, name) != 0)
77 		return -1;
78 	return 0;
79 }
80 
ipc_group_cmd_callback(enum ipc_cmd_status status,const char * line,void * context)81 static void ipc_group_cmd_callback(enum ipc_cmd_status status,
82 				   const char *line, void *context)
83 {
84 	struct ipc_group_cmd *group_cmd = context;
85 
86 	i_assert(group_cmd->refcount > 0);
87 
88 	switch (status) {
89 	case IPC_CMD_STATUS_REPLY:
90 		group_cmd->callback(IPC_CMD_STATUS_REPLY, line,
91 				    group_cmd->context);
92 		break;
93 	case IPC_CMD_STATUS_ERROR:
94 		if (group_cmd->first_error == NULL)
95 			group_cmd->first_error = i_strdup(line);
96 		/* fall through */
97 	case IPC_CMD_STATUS_OK:
98 		if (--group_cmd->refcount > 0)
99 			break;
100 
101 		if (group_cmd->first_error == NULL) {
102 			group_cmd->callback(IPC_CMD_STATUS_OK, line,
103 					    group_cmd->context);
104 		} else {
105 			group_cmd->callback(IPC_CMD_STATUS_ERROR,
106 					    group_cmd->first_error,
107 					    group_cmd->context);
108 			i_free(group_cmd->first_error);
109 		}
110 		i_free(group_cmd);
111 		break;
112 	}
113 
114 }
115 
ipc_group_cmd(struct ipc_group * group,const char * cmd,ipc_cmd_callback_t * callback,void * context)116 bool ipc_group_cmd(struct ipc_group *group, const char *cmd,
117 		   ipc_cmd_callback_t *callback, void *context)
118 {
119 	struct ipc_connection *conn, *next;
120 	struct ipc_group_cmd *group_cmd;
121 
122 	if (group->connections == NULL) {
123 		callback(IPC_CMD_STATUS_OK, NULL, context);
124 		return FALSE;
125 	}
126 
127 	group_cmd = i_new(struct ipc_group_cmd, 1);
128 	group_cmd->callback = callback;
129 	group_cmd->context = context;
130 
131 	for (conn = group->connections; conn != NULL; conn = next) {
132 		next = conn->next;
133 
134 		group_cmd->refcount++;
135 		ipc_connection_cmd(conn, cmd,
136 				   ipc_group_cmd_callback, group_cmd);
137 	}
138 	return TRUE;
139 }
140 
ipc_groups_init(void)141 void ipc_groups_init(void)
142 {
143 	i_array_init(&ipc_groups, 16);
144 }
145 
ipc_groups_disconnect_all(void)146 void ipc_groups_disconnect_all(void)
147 {
148 	struct ipc_group *const *groupp, *group;
149 
150 	while (array_count(&ipc_groups) > 0) {
151 		groupp = array_front(&ipc_groups);
152 		group = *groupp;
153 
154 		while ((*groupp)->connections != NULL) {
155 			struct ipc_connection *conn = (*groupp)->connections;
156 			ipc_connection_destroy(&conn, FALSE, "Shutting down");
157 		}
158 		ipc_group_free(&group);
159 	}
160 }
161 
ipc_groups_deinit(void)162 void ipc_groups_deinit(void)
163 {
164 	ipc_groups_disconnect_all();
165 	array_free(&ipc_groups);
166 }
167