ctdb-tests: Strengthen some tests
[samba.git] / ctdb / common / srvid.c
1 /*
2    Message handler database based on srvid
3
4    Copyright (C) Amitay Isaacs  2015
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "replace.h"
21 #include "system/filesys.h"
22
23 #include <tdb.h>
24
25 #include "lib/util/dlinklist.h"
26 #include "common/db_hash.h"
27 #include "common/srvid.h"
28
29 struct srvid_handler_list;
30
31 struct srvid_context {
32         struct db_hash_context *dh;
33         struct srvid_handler_list *list;
34 };
35
36 struct srvid_handler {
37         struct srvid_handler *prev, *next;
38         struct srvid_handler_list *list;
39         srvid_handler_fn handler;
40         void *private_data;
41 };
42
43 struct srvid_handler_list {
44         struct srvid_handler_list *prev, *next;
45         struct srvid_context *srv;
46         uint64_t srvid;
47         struct srvid_handler *h;
48 };
49
50
51 /*
52  * Initialise message srvid context and database
53  */
54 int srvid_init(TALLOC_CTX *mem_ctx, struct srvid_context **result)
55 {
56         struct srvid_context *srv;
57         int ret;
58
59         srv = talloc_zero(mem_ctx, struct srvid_context);
60         if (srv == NULL) {
61                 return ENOMEM;
62         }
63
64         ret = db_hash_init(srv, "messagedb", 8192, DB_HASH_SIMPLE, &srv->dh);
65         if (ret != 0) {
66                 talloc_free(srv);
67                 return ret;
68         }
69
70         *result = srv;
71         return 0;
72 }
73
74 /*
75  * Wrapper functions to insert/delete/fetch srvid_hander_list
76  */
77
78 static int srvid_insert(struct srvid_context *srv, uint64_t srvid,
79                         struct srvid_handler_list *list)
80 {
81         return db_hash_insert(srv->dh, (uint8_t *)&srvid, sizeof(uint64_t),
82                               (uint8_t *)&list, sizeof(list));
83 }
84
85 static int srvid_delete(struct srvid_context *srv, uint64_t srvid)
86 {
87         return db_hash_delete(srv->dh, (uint8_t *)&srvid, sizeof(uint64_t));
88 }
89
90 static int srvid_fetch_parser(uint8_t *keybuf, size_t keylen,
91                               uint8_t *databuf, size_t datalen,
92                               void *private_data)
93 {
94         struct srvid_handler_list **list =
95                 (struct srvid_handler_list **)private_data;
96
97         if (datalen != sizeof(*list)) {
98                 return EIO;
99         }
100
101         *list = *(struct srvid_handler_list **)databuf;
102         return 0;
103 }
104
105 static int srvid_fetch(struct srvid_context *srv, uint64_t srvid,
106                        struct srvid_handler_list **list)
107 {
108         return db_hash_fetch(srv->dh, (uint8_t *)&srvid, sizeof(uint64_t),
109                              srvid_fetch_parser, list);
110 }
111
112 /*
113  * When a handler is freed, remove it from the list
114  */
115 static int srvid_handler_destructor(struct srvid_handler *h)
116 {
117         struct srvid_handler_list *list = h->list;
118
119         DLIST_REMOVE(list->h, h);
120         if (list->h == NULL) {
121                 talloc_free(list);
122         }
123         return 0;
124 }
125
126 /*
127  * When a list is freed, remove all handlers and remove db entry
128  */
129 static int srvid_handler_list_destructor(struct srvid_handler_list *list)
130 {
131         struct srvid_handler *h;
132
133         while (list->h != NULL) {
134                 h = list->h;
135                 DLIST_REMOVE(list->h, h);
136                 TALLOC_FREE(h);
137         }
138
139         srvid_delete(list->srv, list->srvid);
140         DLIST_REMOVE(list->srv->list, list);
141         return 0;
142 }
143
144 /*
145  * Register a message handler
146  */
147 int srvid_register(struct srvid_context *srv, TALLOC_CTX *mem_ctx,
148                    uint64_t srvid, srvid_handler_fn handler,
149                    void *private_data)
150 {
151         struct srvid_handler_list *list;
152         struct srvid_handler *h;
153         int ret;
154
155         if (srv == NULL) {
156                 return EINVAL;
157         }
158
159         h = talloc_zero(mem_ctx, struct srvid_handler);
160         if (h == NULL) {
161                 return ENOMEM;
162         }
163
164         h->handler = handler;
165         h->private_data = private_data;
166
167         ret = srvid_fetch(srv, srvid, &list);
168         if (ret != 0) {
169                 /* srvid not yet registered */
170                 list = talloc_zero(srv, struct srvid_handler_list);
171                 if (list == NULL) {
172                         talloc_free(h);
173                         return ENOMEM;
174                 }
175
176                 list->srv = srv;
177                 list->srvid = srvid;
178
179                 ret = srvid_insert(srv, srvid, list);
180                 if (ret != 0) {
181                         talloc_free(h);
182                         talloc_free(list);
183                         return ret;
184                 }
185
186                 DLIST_ADD(srv->list, list);
187                 talloc_set_destructor(list, srvid_handler_list_destructor);
188         }
189
190         h->list = list;
191         DLIST_ADD(list->h, h);
192         talloc_set_destructor(h, srvid_handler_destructor);
193         return 0;
194 }
195
196 /*
197  * Deregister a message handler
198  */
199 int srvid_deregister(struct srvid_context *srv, uint64_t srvid,
200                      void *private_data)
201 {
202         struct srvid_handler_list *list;
203         struct srvid_handler *h;
204         int ret;
205
206         ret = srvid_fetch(srv, srvid, &list);
207         if (ret != 0) {
208                 return ret;
209         }
210
211         for (h = list->h; h != NULL; h = h->next) {
212                 if (h->private_data == private_data) {
213                         talloc_free(h);
214                         return 0;
215                 }
216         }
217
218         return ENOENT;
219 }
220
221 /*
222  * Check if a message handler exists
223  */
224 int srvid_exists(struct srvid_context *srv, uint64_t srvid, void *private_data)
225 {
226         struct srvid_handler_list *list;
227         struct srvid_handler *h;
228         int ret;
229
230         ret = srvid_fetch(srv, srvid, &list);
231         if (ret != 0) {
232                 return ret;
233         }
234         if (list->h == NULL) {
235                 return ENOENT;
236         }
237
238         if (private_data != NULL) {
239                 for (h = list->h; h != NULL; h = h->next) {
240                         if (h->private_data == private_data) {
241                                 return 0;
242                         }
243                 }
244
245                 return ENOENT;
246         }
247
248         return 0;
249 }
250
251 /*
252  * Send a message to registered srvid and srvid_all
253  */
254 int srvid_dispatch(struct srvid_context *srv, uint64_t srvid,
255                     uint64_t srvid_all, TDB_DATA data)
256 {
257         struct srvid_handler_list *list;
258         struct srvid_handler *h;
259         int ret;
260
261         ret = srvid_fetch(srv, srvid, &list);
262         if (ret == 0) {
263                 for (h = list->h; h != NULL; h = h->next) {
264                         h->handler(srvid, data, h->private_data);
265                 }
266         }
267
268         if (srvid_all == 0) {
269                 return ret;
270         }
271
272         ret = srvid_fetch(srv, srvid_all, &list);
273         if (ret == 0) {
274                 for (h = list->h; h != NULL; h = h->next) {
275                         h->handler(srvid, data, h->private_data);
276                 }
277         }
278
279         return ret;
280 }