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