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