03dfe7c33d3c85c8245aaf9ee02b5450cb981970
[vpp.git] / svm / svmdb.c
1 /*
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 #include <vppinfra/serialize.h>
43
44 #include "svmdb.h"
45
46 static void local_set_variable_nolock (svmdb_client_t * client,
47                                        svmdb_namespace_t namespace,
48                                        u8 * var, u8 * val, u32 elsize);
49
50 always_inline void
51 region_lock (svm_region_t * rp, int tag)
52 {
53   pthread_mutex_lock (&rp->mutex);
54 #ifdef MUTEX_DEBUG
55   rp->mutex_owner_pid = getpid ();
56   rp->mutex_owner_tag = tag;
57 #endif
58 }
59
60 always_inline void
61 region_unlock (svm_region_t * rp)
62 {
63 #ifdef MUTEX_DEBUG
64   rp->mutex_owner_pid = 0;
65   rp->mutex_owner_tag = 0;
66 #endif
67   pthread_mutex_unlock (&rp->mutex);
68 }
69
70 svmdb_client_t *
71 svmdb_map (svmdb_map_args_t * dba)
72 {
73   svmdb_client_t *client = 0;
74   svm_map_region_args_t *a = 0;
75   svm_region_t *db_rp;
76   void *oldheap;
77   svmdb_shm_hdr_t *hp = 0;
78
79   vec_validate (client, 0);
80   vec_validate (a, 0);
81
82   svm_region_init_chroot_uid_gid (dba->root_path, dba->uid, dba->gid);
83
84   a->root_path = dba->root_path;
85   a->name = "/db";
86   a->size = dba->size ? dba->size : SVMDB_DEFAULT_SIZE;
87   a->flags = SVM_FLAGS_MHEAP;
88   a->uid = dba->uid;
89   a->gid = dba->gid;
90
91   db_rp = client->db_rp = svm_region_find_or_create (a);
92
93   ASSERT (db_rp);
94
95   vec_free (a);
96
97   region_lock (client->db_rp, 10);
98   /* Has someone else set up the shared-memory variable table? */
99   if (db_rp->user_ctx)
100     {
101       client->shm = (void *) db_rp->user_ctx;
102       client->pid = getpid ();
103       region_unlock (client->db_rp);
104       ASSERT (client->shm->version == SVMDB_SHM_VERSION);
105       return (client);
106     }
107   /* Nope, it's our problem... */
108
109   /* Add a bogus client (pid=0) so the svm won't be deallocated */
110   oldheap = svm_push_pvt_heap (db_rp);
111   vec_add1 (client->db_rp->client_pids, 0);
112   svm_pop_heap (oldheap);
113
114   oldheap = svm_push_data_heap (db_rp);
115
116   vec_validate (hp, 0);
117   hp->version = SVMDB_SHM_VERSION;
118   hp->namespaces[SVMDB_NAMESPACE_STRING]
119     = hash_create_string (0, sizeof (uword));
120   hp->namespaces[SVMDB_NAMESPACE_VEC]
121     = hash_create_string (0, sizeof (uword));
122
123   db_rp->user_ctx = hp;
124   client->shm = hp;
125
126   svm_pop_heap (oldheap);
127   region_unlock (client->db_rp);
128   client->pid = getpid ();
129
130   return (client);
131 }
132
133 void
134 svmdb_unmap (svmdb_client_t * client)
135 {
136   ASSERT (client);
137
138   if (!svm_get_root_rp ())
139     return;
140
141   svm_region_unmap ((void *) client->db_rp);
142   svm_region_exit ();
143   vec_free (client);
144 }
145
146 static void
147 notify_value (svmdb_value_t * v, svmdb_action_t a)
148 {
149   int i;
150   int rv;
151   union sigval sv;
152   u32 value;
153   u32 *dead_registrations = 0;
154
155   svmdb_notify_t *np;
156
157   for (i = 0; i < vec_len (v->notifications); i++)
158     {
159       np = vec_elt_at_index (v->notifications, i);
160       if (np->action == a)
161         {
162           value = (np->action << 28) | (np->opaque);
163           sv.sival_ptr = (void *) (uword) value;
164           do
165             {
166               rv = 0;
167               if (sigqueue (np->pid, np->signum, sv) == 0)
168                 break;
169               rv = errno;
170             }
171           while (rv == EAGAIN);
172           if (rv == 0)
173             continue;
174           vec_add1 (dead_registrations, i);
175         }
176     }
177
178   for (i = 0; i < vec_len (dead_registrations); i++)
179     {
180       np = vec_elt_at_index (v->notifications, dead_registrations[i]);
181       clib_warning ("dead reg pid %d sig %d action %d opaque %x",
182                     np->pid, np->signum, np->action, np->opaque);
183       vec_delete (v->notifications, 1, dead_registrations[i]);
184     }
185   vec_free (dead_registrations);
186 }
187
188 int
189 svmdb_local_add_del_notification (svmdb_client_t * client,
190                                   svmdb_notification_args_t * a)
191 {
192   uword *h;
193   void *oldheap;
194   hash_pair_t *hp;
195   svmdb_shm_hdr_t *shm;
196   u8 *dummy_value = 0;
197   svmdb_value_t *value;
198   svmdb_notify_t *np;
199   int i;
200   int rv = 0;
201
202   ASSERT (a->elsize);
203
204   region_lock (client->db_rp, 18);
205   shm = client->shm;
206   oldheap = svm_push_data_heap (client->db_rp);
207
208   h = shm->namespaces[a->nspace];
209
210   hp = hash_get_pair_mem (h, a->var);
211   if (hp == 0)
212     {
213       local_set_variable_nolock (client, a->nspace, (u8 *) a->var,
214                                  dummy_value, a->elsize);
215       /* might have moved */
216       h = shm->namespaces[a->nspace];
217       hp = hash_get_pair_mem (h, a->var);
218       ASSERT (hp);
219     }
220
221   value = pool_elt_at_index (shm->values, hp->value[0]);
222
223   for (i = 0; i < vec_len (value->notifications); i++)
224     {
225       np = vec_elt_at_index (value->notifications, i);
226       if ((np->pid == client->pid)
227           && (np->signum == a->signum)
228           && (np->action == a->action) && (np->opaque == a->opaque))
229         {
230           if (a->add_del == 0 /* delete */ )
231             {
232               vec_delete (value->notifications, 1, i);
233               goto out;
234             }
235           else
236             {                   /* add */
237               clib_warning
238                 ("%s: ignore dup reg pid %d signum %d action %d opaque %x",
239                  a->var, client->pid, a->signum, a->action, a->opaque);
240               rv = -2;
241               goto out;
242             }
243         }
244     }
245   if (a->add_del == 0)
246     {
247       rv = -3;
248       goto out;
249     }
250
251   vec_add2 (value->notifications, np, 1);
252   np->pid = client->pid;
253   np->signum = a->signum;
254   np->action = a->action;
255   np->opaque = a->opaque;
256
257 out:
258   svm_pop_heap (oldheap);
259   region_unlock (client->db_rp);
260   return rv;
261 }
262
263
264 static void
265 local_unset_variable_nolock (svmdb_client_t * client,
266                              svmdb_namespace_t namespace, char *var)
267 {
268   uword *h;
269   svmdb_value_t *oldvalue;
270   hash_pair_t *hp;
271
272   h = client->shm->namespaces[namespace];
273   hp = hash_get_pair_mem (h, var);
274   if (hp)
275     {
276       oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
277       if (vec_len (oldvalue->notifications))
278         notify_value (oldvalue, SVMDB_ACTION_UNSET);
279       /* zero length value means unset */
280       _vec_len (oldvalue->value) = 0;
281     }
282   client->shm->namespaces[namespace] = h;
283 }
284
285 void
286 svmdb_local_unset_string_variable (svmdb_client_t * client, char *var)
287 {
288   void *oldheap;
289
290   region_lock (client->db_rp, 11);
291   oldheap = svm_push_data_heap (client->db_rp);
292   local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
293   svm_pop_heap (oldheap);
294   region_unlock (client->db_rp);
295 }
296
297 static void
298 local_set_variable_nolock (svmdb_client_t * client,
299                            svmdb_namespace_t namespace,
300                            u8 * var, u8 * val, u32 elsize)
301 {
302   uword *h;
303   hash_pair_t *hp;
304   u8 *name;
305   svmdb_shm_hdr_t *shm;
306
307   shm = client->shm;
308   h = shm->namespaces[namespace];
309   hp = hash_get_pair_mem (h, var);
310   if (hp)
311     {
312       svmdb_value_t *oldvalue;
313       oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
314       vec_alloc (oldvalue->value, vec_len (val) * elsize);
315       clib_memcpy (oldvalue->value, val, vec_len (val) * elsize);
316       _vec_len (oldvalue->value) = vec_len (val);
317       notify_value (oldvalue, SVMDB_ACTION_SET);
318     }
319   else
320     {
321       svmdb_value_t *newvalue;
322       pool_get (shm->values, newvalue);
323       memset (newvalue, 0, sizeof (*newvalue));
324       newvalue->elsize = elsize;
325       vec_alloc (newvalue->value, vec_len (val) * elsize);
326       clib_memcpy (newvalue->value, val, vec_len (val) * elsize);
327       _vec_len (newvalue->value) = vec_len (val);
328       name = format (0, "%s%c", var, 0);
329       hash_set_mem (h, name, newvalue - shm->values);
330     }
331   shm->namespaces[namespace] = h;
332 }
333
334 void
335 svmdb_local_set_string_variable (svmdb_client_t * client,
336                                  char *var, char *val)
337 {
338   void *oldheap;
339
340   region_lock (client->db_rp, 12);
341   oldheap = svm_push_data_heap (client->db_rp);
342
343   local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
344
345   local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING,
346                              (u8 *) var, (u8 *) val, 1 /* elsize */ );
347   svm_pop_heap (oldheap);
348   region_unlock (client->db_rp);
349 }
350
351 static u8 *
352 local_get_variable_nolock (svmdb_client_t * client,
353                            svmdb_namespace_t namespace, u8 * var)
354 {
355   uword *h;
356   uword *p;
357   svmdb_shm_hdr_t *shm;
358   svmdb_value_t *oldvalue;
359
360   shm = client->shm;
361   h = shm->namespaces[namespace];
362   p = hash_get_mem (h, var);
363   if (p)
364     {
365       oldvalue = pool_elt_at_index (shm->values, p[0]);
366       notify_value (oldvalue, SVMDB_ACTION_GET);
367       return (oldvalue->value);
368     }
369   return 0;
370 }
371
372 void *
373 svmdb_local_get_variable_reference (svmdb_client_t * client,
374                                     svmdb_namespace_t namespace, char *var)
375 {
376   u8 *rv;
377
378   region_lock (client->db_rp, 19);
379   rv = local_get_variable_nolock (client, namespace, (u8 *) var);
380   region_unlock (client->db_rp);
381   return (void *) rv;
382 }
383
384 char *
385 svmdb_local_get_string_variable (svmdb_client_t * client, char *var)
386 {
387   u8 *rv = 0;
388
389   region_lock (client->db_rp, 13);
390   rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_STRING, (u8 *) var);
391
392   if (rv && vec_len (rv))
393     {
394       rv = format (0, "%s", rv);
395       vec_add1 (rv, 0);
396     }
397   region_unlock (client->db_rp);
398   return ((char *) rv);
399 }
400
401 void
402 svmdb_local_dump_strings (svmdb_client_t * client)
403 {
404   uword *h;
405   u8 *key;
406   u32 value;
407   svmdb_shm_hdr_t *shm = client->shm;
408
409   region_lock (client->db_rp, 14);
410
411   h = client->shm->namespaces[SVMDB_NAMESPACE_STRING];
412
413   /* *INDENT-OFF* */
414   hash_foreach_mem(key, value, h,
415   ({
416     svmdb_value_t *v = pool_elt_at_index (shm->values, value);
417
418     fformat(stdout, "%s: %s\n", key,
419             vec_len(v->value) ? v->value : (u8 *)"(nil)");
420   }));
421   /* *INDENT-ON* */
422   region_unlock (client->db_rp);
423 }
424
425 int
426 svmdb_local_serialize_strings (svmdb_client_t * client, char *filename)
427 {
428   uword *h;
429   u8 *key;
430   u32 value;
431   svmdb_shm_hdr_t *shm = client->shm;
432   serialize_main_t _sm, *sm = &_sm;
433   clib_error_t *error = 0;
434   u8 *sanitized_name = 0;
435   int fd = 0;
436
437   if (strstr (filename, "..") || index (filename, '/'))
438     {
439       error = clib_error_return (0, "Illegal characters in filename '%s'",
440                                  filename);
441       goto out;
442     }
443
444   sanitized_name = format (0, "/tmp/%s%c", filename, 0);
445
446   fd = creat ((char *) sanitized_name, 0644);
447
448   if (fd < 0)
449     {
450       error = clib_error_return_unix (0, "Create '%s'", sanitized_name);
451       goto out;
452     }
453
454   serialize_open_unix_file_descriptor (sm, fd);
455
456   region_lock (client->db_rp, 20);
457
458   h = client->shm->namespaces[SVMDB_NAMESPACE_STRING];
459
460   serialize_likely_small_unsigned_integer (sm, hash_elts (h));
461
462   /* *INDENT-OFF* */
463   hash_foreach_mem(key, value, h,
464   ({
465     svmdb_value_t *v = pool_elt_at_index (shm->values, value);
466
467     /* Omit names with nil values */
468     if (vec_len(v->value))
469       {
470         serialize_cstring (sm, (char *)key);
471         serialize_cstring (sm, (char *)v->value);
472       }
473   }));
474   /* *INDENT-ON* */
475   region_unlock (client->db_rp);
476
477   serialize_close (sm);
478
479 out:
480   if (fd > 0 && close (fd) < 0)
481     error = clib_error_return_unix (0, "close fd %d", fd);
482
483   if (error)
484     {
485       clib_error_report (error);
486       return -1;
487     }
488   return 0;
489 }
490
491 int
492 svmdb_local_unserialize_strings (svmdb_client_t * client, char *filename)
493 {
494   serialize_main_t _sm, *sm = &_sm;
495   void *oldheap;
496   clib_error_t *error = 0;
497   u8 *key, *value;
498   int fd = 0;
499   u32 nelts;
500   int i;
501
502   fd = open (filename, O_RDONLY);
503
504   if (fd < 0)
505     {
506       error = clib_error_return_unix (0, "Failed to open '%s'", filename);
507       goto out;
508     }
509
510   unserialize_open_unix_file_descriptor (sm, fd);
511
512   region_lock (client->db_rp, 21);
513   oldheap = svm_push_data_heap (client->db_rp);
514
515   nelts = unserialize_likely_small_unsigned_integer (sm);
516
517   for (i = 0; i < nelts; i++)
518     {
519       unserialize_cstring (sm, (char **) &key);
520       unserialize_cstring (sm, (char **) &value);
521       local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING,
522                                  key, value, 1 /* elsize */ );
523       vec_free (key);
524       vec_free (value);
525     }
526   svm_pop_heap (oldheap);
527   region_unlock (client->db_rp);
528
529   serialize_close (sm);
530
531 out:
532   if (fd > 0 && close (fd) < 0)
533     error = clib_error_return_unix (0, "close fd %d", fd);
534
535   if (error)
536     {
537       clib_error_report (error);
538       return -1;
539     }
540   return 0;
541 }
542
543 void
544 svmdb_local_unset_vec_variable (svmdb_client_t * client, char *var)
545 {
546   void *oldheap;
547
548   region_lock (client->db_rp, 15);
549   oldheap = svm_push_data_heap (client->db_rp);
550   local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
551   svm_pop_heap (oldheap);
552   region_unlock (client->db_rp);
553 }
554
555 void
556 svmdb_local_set_vec_variable (svmdb_client_t * client,
557                               char *var, void *val_arg, u32 elsize)
558 {
559   u8 *val = (u8 *) val_arg;
560   void *oldheap;
561
562   region_lock (client->db_rp, 16);
563   oldheap = svm_push_data_heap (client->db_rp);
564
565   local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
566   local_set_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var,
567                              val, elsize);
568
569   svm_pop_heap (oldheap);
570   region_unlock (client->db_rp);
571 }
572
573 void *
574 svmdb_local_get_vec_variable (svmdb_client_t * client, char *var, u32 elsize)
575 {
576   u8 *rv = 0;
577   u8 *copy = 0;
578
579   region_lock (client->db_rp, 17);
580
581   rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
582
583   if (rv && vec_len (rv))
584     {
585       /* Make a copy in process-local memory */
586       vec_alloc (copy, vec_len (rv) * elsize);
587       clib_memcpy (copy, rv, vec_len (rv) * elsize);
588       _vec_len (copy) = vec_len (rv);
589       region_unlock (client->db_rp);
590       return (copy);
591     }
592   region_unlock (client->db_rp);
593   return (0);
594 }
595
596 void
597 svmdb_local_dump_vecs (svmdb_client_t * client)
598 {
599   uword *h;
600   u8 *key;
601   u32 value;
602   svmdb_shm_hdr_t *shm;
603
604   region_lock (client->db_rp, 17);
605   shm = client->shm;
606
607   h = client->shm->namespaces[SVMDB_NAMESPACE_VEC];
608
609   /* *INDENT-OFF* */
610   hash_foreach_mem(key, value, h,
611   ({
612     svmdb_value_t *v = pool_elt_at_index (shm->values, value);
613     (void) fformat(stdout, "%s:\n %U (%.2f)\n", key,
614                    format_hex_bytes, v->value,
615                    vec_len(v->value)*v->elsize, ((f64 *)(v->value))[0]);
616   }));
617   /* *INDENT-ON* */
618
619   region_unlock (client->db_rp);
620 }
621
622 void *
623 svmdb_local_find_or_add_vec_variable (svmdb_client_t * client,
624                                       char *var, u32 nbytes)
625 {
626   void *oldheap;
627   u8 *rv = 0;
628
629   region_lock (client->db_rp, 18);
630   oldheap = svm_push_data_heap (client->db_rp);
631
632   rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
633
634   if (rv)
635     {
636       goto out;
637     }
638   else
639     {
640       uword *h;
641       u8 *name;
642       svmdb_shm_hdr_t *shm;
643       svmdb_value_t *newvalue;
644
645       shm = client->shm;
646       h = shm->namespaces[SVMDB_NAMESPACE_VEC];
647
648       pool_get (shm->values, newvalue);
649       memset (newvalue, 0, sizeof (*newvalue));
650       newvalue->elsize = 1;
651       vec_alloc (newvalue->value, nbytes);
652       _vec_len (newvalue->value) = nbytes;
653       name = format (0, "%s%c", var, 0);
654       hash_set_mem (h, name, newvalue - shm->values);
655       shm->namespaces[SVMDB_NAMESPACE_VEC] = h;
656       rv = newvalue->value;
657     }
658
659 out:
660   svm_pop_heap (oldheap);
661   region_unlock (client->db_rp);
662   return (rv);
663 }
664
665 /*
666  * fd.io coding-style-patch-verification: ON
667  *
668  * Local Variables:
669  * eval: (c-set-style "gnu")
670  * End:
671  */