blob: 515bc7d276bdbe6317e32d989721c459aa9f670b [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 *------------------------------------------------------------------
3 * svmdb.c -- simple shared memory database
4 *
5 * Copyright (c) 2009 Cisco and/or its affiliates.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *------------------------------------------------------------------
18 */
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <sys/types.h>
23#include <sys/mman.h>
24#include <sys/stat.h>
25#include <netinet/in.h>
26#include <signal.h>
27#include <pthread.h>
28#include <unistd.h>
29#include <time.h>
30#include <fcntl.h>
31#include <string.h>
32#include <vppinfra/clib.h>
33#include <vppinfra/vec.h>
34#include <vppinfra/hash.h>
35#include <vppinfra/bitmap.h>
36#include <vppinfra/fifo.h>
37#include <vppinfra/time.h>
38#include <vppinfra/mheap.h>
39#include <vppinfra/heap.h>
40#include <vppinfra/pool.h>
41#include <vppinfra/format.h>
42
43#include "svmdb.h"
44
45static void local_set_variable_nolock (svmdb_client_t *client,
46 svmdb_namespace_t namespace,
47 u8 * var, u8 * val, u32 elsize);
48
49always_inline void region_lock(svm_region_t *rp, int tag)
50{
51 pthread_mutex_lock(&rp->mutex);
52#ifdef MUTEX_DEBUG
53 rp->mutex_owner_pid = getpid();
54 rp->mutex_owner_tag = tag;
55#endif
56}
57
58always_inline void region_unlock(svm_region_t *rp)
59{
60#ifdef MUTEX_DEBUG
61 rp->mutex_owner_pid = 0;
62 rp->mutex_owner_tag = 0;
63#endif
64 pthread_mutex_unlock(&rp->mutex);
65}
66
67static svmdb_client_t *svmdb_map_internal (char *root_path, uword size)
68{
69 svmdb_client_t *client = 0;
70 svm_map_region_args_t *a = 0;
71 svm_region_t *db_rp;
72 void *oldheap;
73 svmdb_shm_hdr_t *hp = 0;
74
75 vec_validate (client, 0);
76 vec_validate (a, 0);
77
78 svm_region_init_chroot(root_path);
79
80 a->root_path = root_path;
81 a->name = "/db";
82 a->size = size ? size : SVMDB_DEFAULT_SIZE;
83 a->flags = SVM_FLAGS_MHEAP;
84
85 db_rp = client->db_rp = svm_region_find_or_create (a);
86
87 ASSERT(db_rp);
88
89 vec_free (a);
90
91 region_lock (client->db_rp, 10);
92 /* Has someone else set up the shared-memory variable table? */
93 if (db_rp->user_ctx) {
94 client->shm = (void *) db_rp->user_ctx;
95 client->pid = getpid();
96 region_unlock (client->db_rp);
97 ASSERT (client->shm->version == SVMDB_SHM_VERSION);
98 return (client);
99 }
100 /* Nope, it's our problem... */
101
102 /* Add a bogus client (pid=0) so the svm won't be deallocated */
103 oldheap = svm_push_pvt_heap (db_rp);
104 vec_add1(client->db_rp->client_pids, 0);
105 svm_pop_heap (oldheap);
106
107 oldheap = svm_push_data_heap (db_rp);
108
109 vec_validate(hp, 0);
110 hp->version = SVMDB_SHM_VERSION;
111 hp->namespaces[SVMDB_NAMESPACE_STRING]
112 = hash_create_string(0, sizeof(uword));
113 hp->namespaces[SVMDB_NAMESPACE_VEC]
114 = hash_create_string(0, sizeof(uword));
115
116 db_rp->user_ctx = hp;
117 client->shm = hp;
118
119 svm_pop_heap (oldheap);
120 region_unlock (client->db_rp);
121 client->pid = getpid();
122
123 return (client);
124}
125svmdb_client_t *svmdb_map (void)
126{
127 return svmdb_map_internal (0, 0);
128}
129
130svmdb_client_t *svmdb_map_size (uword size)
131{
132 return svmdb_map_internal (0, size);
133}
134
135svmdb_client_t *svmdb_map_chroot (char *root_path)
136{
137 return svmdb_map_internal (root_path, 0);
138}
139
140svmdb_client_t *svmdb_map_chroot_size (char *root_path, uword size)
141{
142 return svmdb_map_internal (root_path, size);
143}
144
145void svmdb_unmap (svmdb_client_t *client)
146{
147 ASSERT(client);
148
149 if (! svm_get_root_rp())
150 return;
151
152 svm_region_unmap ((void *) client->db_rp);
153 svm_region_exit ();
154 vec_free(client);
155}
156
157static void notify_value (svmdb_value_t * v, svmdb_action_t a)
158{
159 int i;
160 int rv;
161 union sigval sv;
162 u32 value;
163 u32 *dead_registrations = 0;
164
165 svmdb_notify_t *np;
166
167 for (i = 0; i < vec_len (v->notifications); i++) {
168 np = vec_elt_at_index (v->notifications, i);
169 if (np->action == a) {
170 value = (np->action<<28) | (np->opaque);
171 sv.sival_ptr = (void *)(uword)value;
172 do {
173 rv = 0;
174 if (sigqueue (np->pid, np->signum, sv) == 0)
175 break;
176 rv = errno;
177 } while (rv == EAGAIN);
178 if (rv == 0)
179 continue;
180 vec_add1 (dead_registrations, i);
181 }
182 }
183
184 for (i = 0; i < vec_len (dead_registrations); i++) {
185 np = vec_elt_at_index (v->notifications, dead_registrations[i]);
186 clib_warning ("dead reg pid %d sig %d action %d opaque %x",
187 np->pid, np->signum, np->action, np->opaque);
188 vec_delete (v->notifications, 1, dead_registrations[i]);
189 }
190 vec_free (dead_registrations);
191}
192
193int svmdb_local_add_del_notification (svmdb_client_t *client,
194 svmdb_notification_args_t *a)
195{
196 uword *h;
197 void *oldheap;
198 hash_pair_t *hp;
199 svmdb_shm_hdr_t * shm;
200 u8 *dummy_value = 0;
201 svmdb_value_t *value;
202 svmdb_notify_t *np;
203 int i;
204 int rv = 0;
205
206 ASSERT (a->elsize);
207
208 region_lock (client->db_rp, 18);
209 shm = client->shm;
210 oldheap = svm_push_data_heap (client->db_rp);
211
212 h = shm->namespaces[a->nspace];
213
214 hp = hash_get_pair_mem (h, a->var);
215 if (hp == 0) {
216 local_set_variable_nolock (client, a->nspace, (u8 *)a->var,
217 dummy_value, a->elsize);
218 /* might have moved */
219 h = shm->namespaces[a->nspace];
220 hp = hash_get_pair_mem (h, a->var);
221 ASSERT(hp);
222 }
223
224 value = pool_elt_at_index (shm->values, hp->value[0]);
225
226 for (i = 0; i < vec_len (value->notifications); i++) {
227 np = vec_elt_at_index (value->notifications, i);
228 if ((np->pid == client->pid)
229 && (np->signum == a->signum)
230 && (np->action == a->action)
231 && (np->opaque == a->opaque)) {
232 if (a->add_del == 0 /* delete */) {
233 vec_delete (value->notifications, 1, i);
234 goto out;
235 } else { /* add */
236 clib_warning (
237 "%s: ignore dup reg pid %d signum %d action %d opaque %x",
238 a->var, client->pid, a->signum, a->action, a->opaque);
239 rv = -2;
240 goto out;
241 }
242 }
243 }
244 if (a->add_del == 0) {
245 rv = -3;
246 goto out;
247 }
248
249 vec_add2 (value->notifications, np, 1);
250 np->pid = client->pid;
251 np->signum = a->signum;
252 np->action = a->action;
253 np->opaque = a->opaque;
254
255out:
256 svm_pop_heap(oldheap);
257 region_unlock (client->db_rp);
258 return rv;
259}
260
261
262static void local_unset_variable_nolock (svmdb_client_t *client,
263 svmdb_namespace_t namespace,
264 char * var)
265{
266 uword *h;
267 svmdb_value_t *oldvalue;
268 hash_pair_t *hp;
269
270 h = client->shm->namespaces[namespace];
271 hp = hash_get_pair_mem (h, var);
272 if (hp) {
273 oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
274 if (vec_len (oldvalue->notifications))
275 notify_value (oldvalue, SVMDB_ACTION_UNSET);
276 /* zero length value means unset */
277 _vec_len (oldvalue->value) = 0;
278 }
279 client->shm->namespaces[namespace] = h;
280}
281
282void svmdb_local_unset_string_variable (svmdb_client_t *client, char *var)
283{
284 void *oldheap;
285
286 region_lock (client->db_rp, 11);
287 oldheap = svm_push_data_heap (client->db_rp);
288 local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
289 svm_pop_heap(oldheap);
290 region_unlock (client->db_rp);
291}
292
293static void local_set_variable_nolock (svmdb_client_t *client,
294 svmdb_namespace_t namespace,
295 u8 * var, u8 * val, u32 elsize)
296{
297 uword *h;
298 hash_pair_t *hp;
299 u8 *name;
300 svmdb_shm_hdr_t * shm;
301
302 shm = client->shm;
303 h = shm->namespaces[namespace];
304 hp = hash_get_pair_mem (h, var);
305 if (hp) {
306 svmdb_value_t * oldvalue;
307 oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
308 vec_alloc (oldvalue->value, vec_len(val)*elsize);
309 memcpy (oldvalue->value, val, vec_len(val)*elsize);
310 _vec_len (oldvalue->value) = vec_len(val);
311 notify_value (oldvalue, SVMDB_ACTION_SET);
312 } else {
313 svmdb_value_t * newvalue;
314 pool_get (shm->values, newvalue);
315 memset (newvalue, 0, sizeof (*newvalue));
316 newvalue->elsize = elsize;
317 vec_alloc (newvalue->value, vec_len(val)*elsize);
318 memcpy (newvalue->value, val, vec_len(val)*elsize);
319 _vec_len (newvalue->value) = vec_len(val);
320 name = format (0, "%s%c", var, 0);
321 hash_set_mem (h, name, newvalue - shm->values);
322 }
323 shm->namespaces[namespace] = h;
324}
325
326void svmdb_local_set_string_variable (svmdb_client_t *client,
327 char *var, char *val)
328{
329 void *oldheap;
330
331 region_lock (client->db_rp, 12);
332 oldheap = svm_push_data_heap (client->db_rp);
333
334 local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
335
336 local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING,
337 (u8 *) var, (u8 *) val, 1 /* elsize */);
338 svm_pop_heap(oldheap);
339 region_unlock (client->db_rp);
340}
341
342static u8 * local_get_variable_nolock (svmdb_client_t *client,
343 svmdb_namespace_t namespace,
344 u8 * var)
345{
346 uword *h;
347 uword *p;
348 svmdb_shm_hdr_t * shm;
349 svmdb_value_t *oldvalue;
350
351 shm = client->shm;
352 h = shm->namespaces[namespace];
353 p = hash_get_mem (h, var);
354 if (p) {
355 oldvalue = pool_elt_at_index (shm->values, p[0]);
356 notify_value (oldvalue, SVMDB_ACTION_GET);
357 return (oldvalue->value);
358 }
359 return 0;
360}
361
362void *svmdb_local_get_variable_reference (svmdb_client_t *client,
363 svmdb_namespace_t namespace,
364 char *var)
365{
366 u8 *rv;
367
368 region_lock (client->db_rp, 19);
369 rv = local_get_variable_nolock (client, namespace, (u8 *)var);
370 region_unlock (client->db_rp);
371 return (void *)rv;
372}
373
374char *svmdb_local_get_string_variable (svmdb_client_t *client, char *var)
375{
376 u8 *rv = 0;
377
378 region_lock (client->db_rp, 13);
379 rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_STRING, (u8 *) var);
380
381 if (rv && vec_len (rv)) {
382 rv = format (0, "%s", rv);
383 vec_add1(rv, 0);
384 }
385 region_unlock (client->db_rp);
386 return ((char *) rv);
387}
388
389void svmdb_local_dump_strings (svmdb_client_t *client)
390{
391 uword *h;
392 u8 *key;
393 u32 value;
394 svmdb_shm_hdr_t *shm = client->shm;
395
396 region_lock (client->db_rp, 14);
397
398 h = client->shm->namespaces [SVMDB_NAMESPACE_STRING];
399
400 hash_foreach_mem(key, value, h,
401 ({
402 svmdb_value_t *v = pool_elt_at_index (shm->values, value);
403
404 fformat(stdout, "%s: %s\n", key,
405 vec_len(v->value) ? v->value : (u8 *)"(nil)");
406 }));
407 region_unlock (client->db_rp);
408}
409
410void svmdb_local_unset_vec_variable (svmdb_client_t *client, char *var)
411{
412 void *oldheap;
413
414 region_lock (client->db_rp, 15);
415 oldheap = svm_push_data_heap (client->db_rp);
416 local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
417 svm_pop_heap(oldheap);
418 region_unlock (client->db_rp);
419}
420
421void svmdb_local_set_vec_variable (svmdb_client_t *client,
422 char *var, void *val_arg, u32 elsize)
423{
424 u8 *val = (u8 *)val_arg;
425 void *oldheap;
426
427 region_lock (client->db_rp, 16);
428 oldheap = svm_push_data_heap (client->db_rp);
429
430 local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
431 local_set_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var,
432 val, elsize);
433
434 svm_pop_heap(oldheap);
435 region_unlock (client->db_rp);
436}
437
438void *svmdb_local_get_vec_variable (svmdb_client_t *client, char *var,
439 u32 elsize)
440{
441 u8 *rv = 0;
442 u8 *copy = 0;
443
444 region_lock (client->db_rp, 17);
445
446 rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
447
448 if (rv && vec_len(rv)) {
449 /* Make a copy in process-local memory */
450 vec_alloc (copy, vec_len(rv)*elsize);
451 memcpy (copy, rv, vec_len(rv)*elsize);
452 _vec_len(copy) = vec_len(rv);
453 region_unlock (client->db_rp);
454 return (copy);
455 }
456 region_unlock (client->db_rp);
457 return (0);
458}
459
460void svmdb_local_dump_vecs (svmdb_client_t *client)
461{
462 uword *h;
463 u8 *key;
464 u32 value;
465 svmdb_shm_hdr_t *shm;
466
467 region_lock (client->db_rp, 17);
468 shm = client->shm;
469
470 h = client->shm->namespaces [SVMDB_NAMESPACE_VEC];
471
472 hash_foreach_mem(key, value, h,
473 ({
474 svmdb_value_t *v = pool_elt_at_index (shm->values, value);
475 (void) fformat(stdout, "%s:\n %U\n", key,
476 format_hex_bytes, v->value,
477 vec_len(v->value)*v->elsize);
478 }));
479
480 region_unlock (client->db_rp);
481}
482
483void *svmdb_local_find_or_add_vec_variable (svmdb_client_t *client,
484 char *var, u32 nbytes)
485{
486 void *oldheap;
487 u8 *rv = 0;
488
489 region_lock (client->db_rp, 18);
490 oldheap = svm_push_data_heap (client->db_rp);
491
492 rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *)var);
493
494 if (rv) {
495 goto out;
496 } else {
497 uword *h;
498 u8 *name;
499 svmdb_shm_hdr_t * shm;
500 svmdb_value_t * newvalue;
501
502 shm = client->shm;
503 h = shm->namespaces[SVMDB_NAMESPACE_VEC];
504
505 pool_get (shm->values, newvalue);
506 memset (newvalue, 0, sizeof (*newvalue));
507 newvalue->elsize = 1;
508 vec_alloc (newvalue->value, nbytes);
509 _vec_len (newvalue->value) = nbytes;
510 name = format (0, "%s%c", var, 0);
511 hash_set_mem (h, name, newvalue - shm->values);
512 shm->namespaces[SVMDB_NAMESPACE_VEC] = h;
513 rv = newvalue->value;
514 }
515
516out:
517 svm_pop_heap(oldheap);
518 region_unlock (client->db_rp);
519 return (rv);
520}