/*
* Copyright (c) 2015 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
Copyright (c) 2005 Eliot Dresselhaus
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <vppinfra/format.h>
#include <vppinfra/socket.h>
static int verbose;
#define if_verbose(format,args...) \
if (verbose) { clib_warning(format, ## args); }
int
test_socket_main (unformat_input_t * input)
{
clib_socket_t _s = { 0 }, *s = &_s;
char *config;
clib_error_t *error;
s->config = "localhost:22";
s->flags = CLIB_SOCKET_F_IS_CLIENT;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "server %s %=", &config,
&s->flags, CLIB_SOCKET_F_IS_SERVER))
;
else if (unformat (input, "client %s %=", &config,
&s->flags, CLIB_SOCKET_F_IS_CLIENT))
;
else
{
error = clib_error_create ("unknown input `%U'\n",
format_unformat_error, input);
goto done;
}
}
error = clib_socket_init (s);
if (error)
goto done;
if (0)
{
struct
{
int a, b;
} *msg;
msg = clib_socket_tx_add (s, sizeof (msg[0]));
msg->a = 99;
msg->b = 100;
}
else
clib_socket_tx_add_formatted (s, "hello there mr server %d\n", 99);
error = clib_socket_tx (s);
if (error)
goto done;
while (1)
{
error = clib_socket_rx (s, 100);
if (error)
break;
if (clib_socket_rx_end_of_file (s))
break;
if_verbose ("/*
*------------------------------------------------------------------
* svmdb.c -- simple shared memory database
*
* Copyright (c) 2009 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <vppinfra/clib.h>
#include <vppinfra/vec.h>
#include <vppinfra/hash.h>
#include <vppinfra/bitmap.h>
#include <vppinfra/fifo.h>
#include <vppinfra/time.h>
#include <vppinfra/heap.h>
#include <vppinfra/pool.h>
#include <vppinfra/format.h>
#include <vppinfra/serialize.h>
#include "svmdb.h"
static void local_set_variable_nolock (svmdb_client_t * client,
svmdb_namespace_t namespace,
u8 * var, u8 * val, u32 elsize);
always_inline void
region_lock (svm_region_t * rp, int tag)
{
pthread_mutex_lock (&rp->mutex);
#ifdef MUTEX_DEBUG
rp->mutex_owner_pid = getpid ();
rp->mutex_owner_tag = tag;
#endif
}
always_inline void
region_unlock (svm_region_t * rp)
{
#ifdef MUTEX_DEBUG
rp->mutex_owner_pid = 0;
rp->mutex_owner_tag = 0;
#endif
pthread_mutex_unlock (&rp->mutex);
}
svmdb_client_t *
svmdb_map (svmdb_map_args_t * dba)
{
svmdb_client_t *client = 0;
svm_map_region_args_t *a = 0;
svm_region_t *db_rp;
void *oldheap;
svmdb_shm_hdr_t *hp = 0;
vec_validate (client, 0);
vec_validate (a, 0);
svm_region_init_chroot_uid_gid (dba->root_path, dba->uid, dba->gid);
a->root_path = dba->root_path;
a->name = "/db";
a->size = dba->size ? dba->size : SVMDB_DEFAULT_SIZE;
a->flags = SVM_FLAGS_MHEAP;
a->uid = dba->uid;
a->gid = dba->gid;
db_rp = client->db_rp = svm_region_find_or_create (a);
ASSERT (db_rp);
vec_free (a);
region_lock (client->db_rp, 10);
/* Has someone else set up the shared-memory variable table? */
if (db_rp->user_ctx)
{
client->shm = (void *) db_rp->user_ctx;
client->pid = getpid ();
region_unlock (client->db_rp);
ASSERT (client->shm->version == SVMDB_SHM_VERSION);
return (client);
}
/* Nope, it's our problem... */
if (CLIB_DEBUG > 2)
{
/* Add a bogus client (pid=0) so the svm won't be deallocated */
clib_warning
("[%d] adding fake client (pid=0) so '%s' won't be unlinked",
getpid (), db_rp->region_name);
oldheap = svm_push_pvt_heap (db_rp);
vec_add1 (client->db_rp->client_pids, 0);
svm_pop_heap (oldheap);
}
oldheap = svm_push_data_heap (db_rp);
vec_validate (hp, 0);
hp->version = SVMDB_SHM_VERSION;
hp->namespaces[SVMDB_NAMESPACE_STRING]
= hash_create_string (0, sizeof (uword));
hp->namespaces[SVMDB_NAMESPACE_VEC]
= hash_create_string (0, sizeof (uword));
db_rp->user_ctx = hp;
client->shm = hp;
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
client->pid = getpid ();
return (client);
}
void
svmdb_unmap (svmdb_client_t * client)
{
ASSERT (client);
if (!svm_get_root_rp ())
return;
svm_region_unmap ((void *) client->db_rp);
svm_region_exit ();
vec_free (client);
}
static void
notify_value (svmdb_value_t * v, svmdb_action_t a)
{
int i;
int rv;
union sigval sv;
u32 value;
u32 *dead_registrations = 0;
svmdb_notify_t *np;
for (i = 0; i < vec_len (v->notifications); i++)
{
np = vec_elt_at_index (v->notifications, i);
if (np->action == a)
{
value = (np->action << 28) | (np->opaque);
sv.sival_ptr = (void *) (uword) value;
do
{
rv = 0;
if (sigqueue (np->pid, np->signum, sv) == 0)
break;
rv = errno;
}
while (rv == EAGAIN);
if (rv == 0)
continue;
vec_add1 (dead_registrations, i);
}
}
for (i = 0; i < vec_len (dead_registrations); i++)
{
np = vec_elt_at_index (v->notifications, dead_registrations[i]);
clib_warning ("dead reg pid %d sig %d action %d opaque %x",
np->pid, np->signum, np->action, np->opaque);
vec_delete (v->notifications, 1, dead_registrations[i]);
}
vec_free (dead_registrations);
}
int
svmdb_local_add_del_notification (svmdb_client_t * client,
svmdb_notification_args_t * a)
{
uword *h;
void *oldheap;
hash_pair_t *hp;
svmdb_shm_hdr_t *shm;
u8 *placeholder_value = 0;
svmdb_value_t *value;
svmdb_notify_t *np;
int i;
int rv = 0;
ASSERT (a->elsize);
region_lock (client->db_rp, 18);
shm = client->shm;
oldheap = svm_push_data_heap (client->db_rp);
h = shm->namespaces[a->nspace];
hp = hash_get_pair_mem (h, a->var);
if (hp == 0)
{
local_set_variable_nolock (client, a->nspace, (u8 *) a->var,
placeholder_value, a->elsize);
/* might have moved */
h = shm->namespaces[a->nspace];
hp = hash_get_pair_mem (h, a->var);
ASSERT (hp);
}
value = pool_elt_at_index (shm->values, hp->value[0]);
for (i = 0; i < vec_len (value->notifications); i++)
{
np = vec_elt_at_index (value->notifications, i);
if ((np->pid == client->pid)
&& (np->signum == a->signum)
&& (np->action == a->action) && (np->opaque == a->opaque))
{
if (a->add_del == 0 /* delete */ )
{
vec_delete (value->notifications, 1, i);
goto out;
}
else
{ /* add */
clib_warning
("%s: ignore dup reg pid %d signum %d action %d opaque %x",
a->var, client->pid, a->signum, a->action, a->opaque);
rv = -2;
goto out;
}
}
}
if (a->add_del == 0)
{
rv = -3;
goto out;
}
vec_add2 (value->notifications, np, 1);
np->pid = client->pid;
np->signum = a->signum;
np->action = a->action;
np->opaque = a->opaque;
out:
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
return rv;
}
static void
local_unset_variable_nolock (svmdb_client_t * client,
svmdb_namespace_t namespace, char *var)
{
uword *h;
svmdb_value_t *oldvalue;
hash_pair_t *hp;
h = client->shm->namespaces[namespace];
hp = hash_get_pair_mem (h, var);
if (hp)
{
oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
if (vec_len (oldvalue->notifications))
notify_value (oldvalue, SVMDB_ACTION_UNSET);
/* zero length value means unset */
_vec_len (oldvalue->value) = 0;
}
client->shm->namespaces[namespace] = h;
}
void
svmdb_local_unset_string_variable (svmdb_client_t * client, char *var)
{
void *oldheap;
region_lock (client->db_rp, 11);
oldheap = svm_push_data_heap (client->db_rp);
local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
}
static void
local_set_variable_nolock (svmdb_client_t * client,
svmdb_namespace_t namespace,
u8 * var, u8 * val, u32 elsize)
{
uword *h;
hash_pair_t *hp;
u8 *name;
svmdb_shm_hdr_t *shm;
shm = client->shm;
h = shm->namespaces[namespace];
hp = hash_get_pair_mem (h, var);
if (hp)
{
svmdb_value_t *oldvalue;
oldvalue = pool_elt_at_index (client->shm->values, hp->value[0]);
vec_alloc (oldvalue->value, vec_len (val) * elsize);
clib_memcpy (oldvalue->value, val, vec_len (val) * elsize);
_vec_len (oldvalue->value) = vec_len (val);
notify_value (oldvalue, SVMDB_ACTION_SET);
}
else
{
svmdb_value_t *newvalue;
pool_get (shm->values, newvalue);
clib_memset (newvalue, 0, sizeof (*newvalue));
newvalue->elsize = elsize;
vec_alloc (newvalue->value, vec_len (val) * elsize);
clib_memcpy (newvalue->value, val, vec_len (val) * elsize);
_vec_len (newvalue->value) = vec_len (val);
name = format (0, "%s%c", var, 0);
hash_set_mem (h, name, newvalue - shm->values);
}
shm->namespaces[namespace] = h;
}
void
svmdb_local_set_string_variable (svmdb_client_t * client,
char *var, char *val)
{
void *oldheap;
region_lock (client->db_rp, 12);
oldheap = svm_push_data_heap (client->db_rp);
local_unset_variable_nolock (client, SVMDB_NAMESPACE_STRING, var);
local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING,
(u8 *) var, (u8 *) val, 1 /* elsize */ );
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
}
static u8 *
local_get_variable_nolock (svmdb_client_t * client,
svmdb_namespace_t namespace, u8 * var)
{
uword *h;
uword *p;
svmdb_shm_hdr_t *shm;
svmdb_value_t *oldvalue;
shm = client->shm;
h = shm->namespaces[namespace];
p = hash_get_mem (h, var);
if (p)
{
oldvalue = pool_elt_at_index (shm->values, p[0]);
notify_value (oldvalue, SVMDB_ACTION_GET);
return (oldvalue->value);
}
return 0;
}
void *
svmdb_local_get_variable_reference (svmdb_client_t * client,
svmdb_namespace_t namespace, char *var)
{
u8 *rv;
region_lock (client->db_rp, 19);
rv = local_get_variable_nolock (client, namespace, (u8 *) var);
region_unlock (client->db_rp);
return (void *) rv;
}
char *
svmdb_local_get_string_variable (svmdb_client_t * client, char *var)
{
u8 *rv = 0;
region_lock (client->db_rp, 13);
rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_STRING, (u8 *) var);
if (rv && vec_len (rv))
{
rv = format (0, "%s", rv);
vec_add1 (rv, 0);
}
region_unlock (client->db_rp);
return ((char *) rv);
}
void
svmdb_local_dump_strings (svmdb_client_t * client)
{
uword *h;
u8 *key;
u32 value;
svmdb_shm_hdr_t *shm = client->shm;
region_lock (client->db_rp, 14);
h = client->shm->namespaces[SVMDB_NAMESPACE_STRING];
/* *INDENT-OFF* */
hash_foreach_mem(key, value, h,
({
svmdb_value_t *v = pool_elt_at_index (shm->values, value);
fformat(stdout, "%s: %s\n", key,
vec_len(v->value) ? v->value : (u8 *)"(nil)");
}));
/* *INDENT-ON* */
region_unlock (client->db_rp);
}
int
svmdb_local_serialize_strings (svmdb_client_t * client, char *filename)
{
uword *h;
u8 *key;
u32 value;
svmdb_shm_hdr_t *shm = client->shm;
serialize_main_t _sm, *sm = &_sm;
clib_error_t *error = 0;
u8 *sanitized_name = 0;
int fd = 0;
if (strstr (filename, "..") || index (filename, '/'))
{
error = clib_error_return (0, "Illegal characters in filename '%s'",
filename);
goto out;
}
sanitized_name = format (0, "/tmp/%s%c", filename, 0);
fd = creat ((char *) sanitized_name, 0644);
if (fd < 0)
{
error = clib_error_return_unix (0, "Create '%s'", sanitized_name);
goto out;
}
serialize_open_clib_file_descriptor (sm, fd);
region_lock (client->db_rp, 20);
h = client->shm->namespaces[SVMDB_NAMESPACE_STRING];
serialize_likely_small_unsigned_integer (sm, hash_elts (h));
/* *INDENT-OFF* */
hash_foreach_mem(key, value, h,
({
svmdb_value_t *v = pool_elt_at_index (shm->values, value);
/* Omit names with nil values */
if (vec_len(v->value))
{
serialize_cstring (sm, (char *)key);
serialize_cstring (sm, (char *)v->value);
}
}));
/* *INDENT-ON* */
region_unlock (client->db_rp);
serialize_close (sm);
out:
if (fd > 0 && close (fd) < 0)
error = clib_error_return_unix (0, "close fd %d", fd);
if (error)
{
clib_error_report (error);
return -1;
}
return 0;
}
int
svmdb_local_unserialize_strings (svmdb_client_t * client, char *filename)
{
serialize_main_t _sm, *sm = &_sm;
void *oldheap;
clib_error_t *error = 0;
u8 *key, *value;
int fd = 0;
u32 nelts;
int i;
fd = open (filename, O_RDONLY);
if (fd < 0)
{
error = clib_error_return_unix (0, "Failed to open '%s'", filename);
goto out;
}
unserialize_open_clib_file_descriptor (sm, fd);
region_lock (client->db_rp, 21);
oldheap = svm_push_data_heap (client->db_rp);
nelts = unserialize_likely_small_unsigned_integer (sm);
for (i = 0; i < nelts; i++)
{
unserialize_cstring (sm, (char **) &key);
unserialize_cstring (sm, (char **) &value);
local_set_variable_nolock (client, SVMDB_NAMESPACE_STRING,
key, value, 1 /* elsize */ );
vec_free (key);
vec_free (value);
}
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
serialize_close (sm);
out:
if (fd > 0 && close (fd) < 0)
error = clib_error_return_unix (0, "close fd %d", fd);
if (error)
{
clib_error_report (error);
return -1;
}
return 0;
}
void
svmdb_local_unset_vec_variable (svmdb_client_t * client, char *var)
{
void *oldheap;
region_lock (client->db_rp, 15);
oldheap = svm_push_data_heap (client->db_rp);
local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
}
void
svmdb_local_set_vec_variable (svmdb_client_t * client,
char *var, void *val_arg, u32 elsize)
{
u8 *val = (u8 *) val_arg;
void *oldheap;
region_lock (client->db_rp, 16);
oldheap = svm_push_data_heap (client->db_rp);
local_unset_variable_nolock (client, SVMDB_NAMESPACE_VEC, var);
local_set_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var,
val, elsize);
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
}
void *
svmdb_local_get_vec_variable (svmdb_client_t * client, char *var, u32 elsize)
{
u8 *rv = 0;
u8 *copy = 0;
region_lock (client->db_rp, 17);
rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
if (rv && vec_len (rv))
{
/* Make a copy in process-local memory */
vec_alloc (copy, vec_len (rv) * elsize);
clib_memcpy (copy, rv, vec_len (rv) * elsize);
_vec_len (copy) = vec_len (rv);
region_unlock (client->db_rp);
return (copy);
}
region_unlock (client->db_rp);
return (0);
}
void
svmdb_local_dump_vecs (svmdb_client_t * client)
{
uword *h;
u8 *key;
u32 value;
svmdb_shm_hdr_t *shm;
region_lock (client->db_rp, 17);
shm = client->shm;
h = client->shm->namespaces[SVMDB_NAMESPACE_VEC];
/* *INDENT-OFF* */
hash_foreach_mem(key, value, h,
({
svmdb_value_t *v = pool_elt_at_index (shm->values, value);
(void) fformat(stdout, "%s:\n %U (%.2f)\n", key,
format_hex_bytes, v->value,
vec_len(v->value)*v->elsize, ((f64 *)(v->value))[0]);
}));
/* *INDENT-ON* */
region_unlock (client->db_rp);
}
void *
svmdb_local_find_or_add_vec_variable (svmdb_client_t * client,
char *var, u32 nbytes)
{
void *oldheap;
u8 *rv = 0;
region_lock (client->db_rp, 18);
oldheap = svm_push_data_heap (client->db_rp);
rv = local_get_variable_nolock (client, SVMDB_NAMESPACE_VEC, (u8 *) var);
if (rv)
{
goto out;
}
else
{
uword *h;
u8 *name;
svmdb_shm_hdr_t *shm;
svmdb_value_t *newvalue;
shm = client->shm;
h = shm->namespaces[SVMDB_NAMESPACE_VEC];
pool_get (shm->values, newvalue);
clib_memset (newvalue, 0, sizeof (*newvalue));
newvalue->elsize = 1;
vec_alloc (newvalue->value, nbytes);
_vec_len (newvalue->value) = nbytes;
name = format (0, "%s%c", var, 0);
hash_set_mem (h, name, newvalue - shm->values);
shm->namespaces[SVMDB_NAMESPACE_VEC] = h;
rv = newvalue->value;
}
out:
svm_pop_heap (oldheap);
region_unlock (client->db_rp);
return (rv);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/