1e65ce25426789f71cd496876cff3fad153119bf
[sfrench/samba-autobuild/.git] / lib / util / server_id_db.c
1 /*
2  * Map names to server_ids
3  *
4  * Copyright Volker Lendecke <vl@samba.org> 2014
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 #include "lib/util/server_id_db.h"
23 #include "lib/tdb_wrap/tdb_wrap.h"
24 #include "lib/util/strv.h"
25 #include "lib/util/util_tdb.h"
26 #include "lib/util/samba_util.h"
27
28 static TDB_DATA talloc_tdb_data(void *ptr)
29 {
30         return (TDB_DATA) { .dptr = ptr, .dsize = talloc_get_size(ptr) };
31 }
32
33 struct server_id_db {
34         struct server_id pid;
35         struct tdb_wrap *tdb;
36         char *names;
37 };
38
39 static int server_id_db_destructor(struct server_id_db *db);
40
41 struct server_id_db *server_id_db_init(TALLOC_CTX *mem_ctx,
42                                        struct server_id pid,
43                                        const char *base_path,
44                                        int hash_size, int tdb_flags)
45 {
46         struct server_id_db *db;
47         size_t pathlen = strlen(base_path) + 11;
48         char path[pathlen];
49
50         db = talloc(mem_ctx, struct server_id_db);
51         if (db == NULL) {
52                 return NULL;
53         }
54         db->pid = pid;
55         db->names = NULL;
56
57         snprintf(path, pathlen, "%s/names.tdb", base_path);
58
59         db->tdb = tdb_wrap_open(db, path, hash_size, tdb_flags,
60                                 O_RDWR|O_CREAT, 0660);
61         if (db->tdb == NULL) {
62                 TALLOC_FREE(db);
63                 return NULL;
64         }
65
66         talloc_set_destructor(db, server_id_db_destructor);
67
68         return db;
69 }
70
71 void server_id_db_reinit(struct server_id_db *db, struct server_id pid)
72 {
73         db->pid = pid;
74         TALLOC_FREE(db->names);
75 }
76
77 struct server_id server_id_db_pid(struct server_id_db *db)
78 {
79         return db->pid;
80 }
81
82 static int server_id_db_destructor(struct server_id_db *db)
83 {
84         char *name = NULL;
85
86         while ((name = strv_next(db->names, name)) != NULL) {
87                 server_id_db_remove(db, name);
88         }
89
90         return 0;
91 }
92
93 int server_id_db_add(struct server_id_db *db, const char *name)
94 {
95         struct tdb_context *tdb = db->tdb->tdb;
96         TDB_DATA key;
97         char *n;
98         int ret;
99
100         n = strv_find(db->names, name);
101         if (n != NULL) {
102                 return EEXIST;
103         }
104
105         ret = strv_add(db, &db->names, name);
106         if (ret != 0) {
107                 return ret;
108         }
109
110         key = string_term_tdb_data(name);
111
112         {
113                 size_t idlen = server_id_str_buf_unique(db->pid, NULL, 0);
114                 char idbuf[idlen];
115
116                 server_id_str_buf_unique(db->pid, idbuf, idlen);
117
118                 ret = tdb_append(
119                         tdb, key,
120                         (TDB_DATA) { .dptr = (uint8_t *)idbuf, .dsize = idlen });
121         }
122
123         if (ret != 0) {
124                 enum TDB_ERROR err = tdb_error(tdb);
125                 strv_delete(&db->names, strv_find(db->names, name));
126                 return map_unix_error_from_tdb(err);
127         }
128
129         return 0;
130 }
131
132 int server_id_db_prune_name(struct server_id_db *db, const char *name,
133                             struct server_id server)
134 {
135         struct tdb_context *tdb = db->tdb->tdb;
136         size_t idbuf_len = server_id_str_buf_unique(server, NULL, 0);
137         char idbuf[idbuf_len];
138         TDB_DATA key;
139         uint8_t *data;
140         char *ids, *id;
141         int ret;
142
143         key = string_term_tdb_data(name);
144         server_id_str_buf_unique(server, idbuf, idbuf_len);
145
146         ret = tdb_chainlock(tdb, key);
147         if (ret == -1) {
148                 enum TDB_ERROR err = tdb_error(tdb);
149                 return map_unix_error_from_tdb(err);
150         }
151
152         ret = tdb_fetch_talloc(tdb, key, db, &data);
153         if (ret != 0) {
154                 tdb_chainunlock(tdb, key);
155                 return ret;
156         }
157
158         ids = (char *)data;
159
160         id = strv_find(ids, idbuf);
161         if (id == NULL) {
162                 tdb_chainunlock(tdb, key);
163                 TALLOC_FREE(data);
164                 return ENOENT;
165         }
166
167         strv_delete(&ids, id);
168         ret = tdb_store(tdb, key, talloc_tdb_data(ids), TDB_MODIFY);
169         TALLOC_FREE(data);
170
171         tdb_chainunlock(tdb, key);
172
173         return 0;
174 }
175
176 int server_id_db_remove(struct server_id_db *db, const char *name)
177 {
178         char *n;
179         int ret;
180
181         n = strv_find(db->names, name);
182         if (n == NULL) {
183                 return ENOENT;
184         }
185
186         ret = server_id_db_prune_name(db, name, db->pid);
187         if (ret != 0) {
188                 return ret;
189         }
190
191         strv_delete(&db->names, n);
192         return 0;
193 }
194
195 int server_id_db_lookup(struct server_id_db *db, const char *name,
196                         TALLOC_CTX *mem_ctx, unsigned *pnum_servers,
197                         struct server_id **pservers)
198 {
199         struct tdb_context *tdb = db->tdb->tdb;
200         TDB_DATA key;
201         uint8_t *data;
202         char *ids, *id;
203         unsigned num_servers;
204         struct server_id *servers;
205         int i, ret;
206
207         key = string_term_tdb_data(name);
208
209         ret = tdb_fetch_talloc(tdb, key, mem_ctx, &data);
210         if (ret != 0) {
211                 return ret;
212         }
213
214         ids = (char *)data;
215         num_servers = strv_count(ids);
216
217         servers = talloc_array(mem_ctx, struct server_id, num_servers);
218         if (servers == NULL) {
219                 TALLOC_FREE(data);
220                 return ENOMEM;
221         }
222
223         i = 0;
224
225         for (id = ids; id != NULL; id = strv_next(ids, id)) {
226                 servers[i++] = server_id_from_string(NONCLUSTER_VNN, id);
227         }
228
229         TALLOC_FREE(data);
230
231         *pnum_servers = num_servers;
232         *pservers = servers;
233
234         return 0;
235 }
236
237 bool server_id_db_lookup_one(struct server_id_db *db, const char *name,
238                              struct server_id *server)
239 {
240         int ret;
241         unsigned num_servers;
242         struct server_id *servers;
243
244         ret = server_id_db_lookup(db, name, db, &num_servers, &servers);
245         if (ret != 0) {
246                 return false;
247         }
248         if (num_servers == 0) {
249                 TALLOC_FREE(servers);
250                 return false;
251         }
252         *server = servers[0];
253         TALLOC_FREE(servers);
254         return true;
255 }
256
257 struct server_id_db_traverse_state {
258         TALLOC_CTX *mem_ctx;
259         int (*fn)(const char *name,
260                   unsigned num_servers,
261                   const struct server_id *servers,
262                   void *private_data);
263         void *private_data;
264 };
265
266 static int server_id_db_traverse_fn(struct tdb_context *tdb,
267                                     TDB_DATA key, TDB_DATA data,
268                                     void *private_data)
269 {
270         struct server_id_db_traverse_state *state = private_data;
271         const char *name;
272         char *ids, *id;
273         unsigned num_servers;
274         struct server_id *servers;
275         int i, ret;
276
277         if (key.dsize == 0) {
278                 return 0;
279         }
280         if (key.dptr[key.dsize-1] != '\0') {
281                 return 0;
282         }
283         name = (const char *)key.dptr;
284
285         ids = (char *)talloc_memdup(state->mem_ctx, data.dptr, data.dsize);
286         if (ids == NULL) {
287                 return 0;
288         }
289
290         num_servers = strv_count(ids);
291         servers = talloc_array(ids, struct server_id, num_servers);
292
293         i = 0;
294
295         for (id = ids; id != NULL; id = strv_next(ids, id)) {
296                 servers[i++] = server_id_from_string(NONCLUSTER_VNN, id);
297         }
298
299         ret = state->fn(name, num_servers, servers, state->private_data);
300
301         TALLOC_FREE(ids);
302
303         return ret;
304 }
305
306 int server_id_db_traverse_read(struct server_id_db *db,
307                                int (*fn)(const char *name,
308                                          unsigned num_servers,
309                                          const struct server_id *servers,
310                                          void *private_data),
311                                void *private_data)
312 {
313         struct server_id_db_traverse_state state;
314         int ret;
315
316         state = (struct server_id_db_traverse_state) {
317                 .fn = fn, .private_data = private_data,
318                 .mem_ctx = talloc_new(db)
319         };
320
321         if (state.mem_ctx == NULL) {
322                 return ENOMEM;
323         }
324
325         ret = tdb_traverse_read(db->tdb->tdb, server_id_db_traverse_fn,
326                                 &state);
327         TALLOC_FREE(state.mem_ctx);
328         return ret;
329 }