|
|
e0c9be |
diff -urNp old/agent/mibgroup/ip-mib/data_access/ipaddress_common.c new/agent/mibgroup/ip-mib/data_access/ipaddress_common.c
|
|
|
e0c9be |
--- old/agent/mibgroup/ip-mib/data_access/ipaddress_common.c 2012-10-10 00:28:58.000000000 +0200
|
|
|
e0c9be |
+++ new/agent/mibgroup/ip-mib/data_access/ipaddress_common.c 2017-04-04 11:02:42.391951747 +0200
|
|
|
e0c9be |
@@ -67,6 +67,7 @@ netsnmp_container *
|
|
|
e0c9be |
netsnmp_access_ipaddress_container_init(u_int flags)
|
|
|
e0c9be |
{
|
|
|
e0c9be |
netsnmp_container *container1;
|
|
|
e0c9be |
+ int rc;
|
|
|
e0c9be |
|
|
|
e0c9be |
DEBUGMSGTL(("access:ipaddress:container", "init\n"));
|
|
|
e0c9be |
|
|
|
e0c9be |
@@ -80,6 +81,7 @@ netsnmp_access_ipaddress_container_init(
|
|
|
e0c9be |
return NULL;
|
|
|
e0c9be |
}
|
|
|
e0c9be |
container1->container_name = strdup("ia_index");
|
|
|
e0c9be |
+ CONTAINER_SET_OPTIONS(container1, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
|
|
|
e0c9be |
|
|
|
e0c9be |
if (flags & NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR) {
|
|
|
e0c9be |
netsnmp_container *container2 =
|
|
|
e0c9be |
@@ -92,6 +94,13 @@ netsnmp_access_ipaddress_container_init(
|
|
|
e0c9be |
|
|
|
e0c9be |
container2->compare = _access_ipaddress_entry_compare_addr;
|
|
|
e0c9be |
container2->container_name = strdup("ia_addr");
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * With allowed duplicates, CONTAINER_INSERT does not need to sort whole
|
|
|
e0c9be |
+ * container and check for duplicates. We remove duplicates manually in
|
|
|
e0c9be |
+ * netsnmp_access_ipaddress_container_load.
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+ CONTAINER_SET_OPTIONS(container2, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
|
|
|
e0c9be |
|
|
|
e0c9be |
netsnmp_container_add_index(container1, container2);
|
|
|
e0c9be |
}
|
|
|
e0c9be |
@@ -100,6 +109,53 @@ netsnmp_access_ipaddress_container_init(
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
/**
|
|
|
e0c9be |
+ * Remove duplicate entries from the container.
|
|
|
e0c9be |
+ * This function returns new copy of the container and destroys
|
|
|
e0c9be |
+ * the original one. Use like this:
|
|
|
e0c9be |
+ * c = _remove_duplicates(c, flags);
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+static netsnmp_container *
|
|
|
e0c9be |
+_remove_duplicates(netsnmp_container *container, u_int container_flags)
|
|
|
e0c9be |
+{
|
|
|
e0c9be |
+ netsnmp_container *c;
|
|
|
e0c9be |
+ netsnmp_iterator *it;
|
|
|
e0c9be |
+ netsnmp_container *ret;
|
|
|
e0c9be |
+ netsnmp_ipaddress_entry *entry, *prev_entry;
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ if (! (container_flags & NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR)) {
|
|
|
e0c9be |
+ /* We don't have address index, we can't detect duplicates */
|
|
|
e0c9be |
+ return container;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ ret = netsnmp_access_ipaddress_container_init(container_flags);
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ /* use the IpAddress index */
|
|
|
e0c9be |
+ c = container->next;
|
|
|
e0c9be |
+ it = CONTAINER_ITERATOR(c);
|
|
|
e0c9be |
+ /* Sort the address index */
|
|
|
e0c9be |
+ CONTAINER_FIND(c, ITERATOR_FIRST(it));
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * Sequentially iterate over sorted container and add only unique entries
|
|
|
e0c9be |
+ * to 'ret'
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+ prev_entry = NULL;
|
|
|
e0c9be |
+ for (entry = ITERATOR_FIRST(it); entry; entry = ITERATOR_NEXT(it)) {
|
|
|
e0c9be |
+ if (prev_entry && _access_ipaddress_entry_compare_addr(prev_entry, entry) == 0) {
|
|
|
e0c9be |
+ /* 'entry' is duplicate of the previous one -> delete it */
|
|
|
e0c9be |
+ netsnmp_access_ipaddress_entry_free(entry);
|
|
|
e0c9be |
+ } else {
|
|
|
e0c9be |
+ CONTAINER_INSERT(ret, entry);
|
|
|
e0c9be |
+ prev_entry = entry;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ CONTAINER_FREE(container);
|
|
|
e0c9be |
+ free(it);
|
|
|
e0c9be |
+ return ret;
|
|
|
e0c9be |
+}
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+/**
|
|
|
e0c9be |
* @retval NULL error
|
|
|
e0c9be |
* @retval !NULL pointer to container
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
@@ -112,9 +168,10 @@ netsnmp_access_ipaddress_container_load(
|
|
|
e0c9be |
|
|
|
e0c9be |
DEBUGMSGTL(("access:ipaddress:container", "load\n"));
|
|
|
e0c9be |
|
|
|
e0c9be |
+ if (load_flags & NETSNMP_ACCESS_IPADDRESS_LOAD_ADDL_IDX_BY_ADDR)
|
|
|
e0c9be |
+ container_flags |= NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR;
|
|
|
e0c9be |
+
|
|
|
e0c9be |
if (NULL == container) {
|
|
|
e0c9be |
- if (load_flags & NETSNMP_ACCESS_IPADDRESS_LOAD_ADDL_IDX_BY_ADDR)
|
|
|
e0c9be |
- container_flags |= NETSNMP_ACCESS_IPADDRESS_INIT_ADDL_IDX_BY_ADDR;
|
|
|
e0c9be |
container = netsnmp_access_ipaddress_container_init(container_flags);
|
|
|
e0c9be |
}
|
|
|
e0c9be |
if (NULL == container) {
|
|
|
e0c9be |
@@ -129,6 +186,9 @@ netsnmp_access_ipaddress_container_load(
|
|
|
e0c9be |
container = NULL;
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
+ if (container)
|
|
|
e0c9be |
+ container = _remove_duplicates(container, container_flags);
|
|
|
e0c9be |
+
|
|
|
e0c9be |
return container;
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
diff -urNp old/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c new/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c
|
|
|
e0c9be |
--- old/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c 2012-10-10 00:28:58.000000000 +0200
|
|
|
e0c9be |
+++ new/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable_data_access.c 2017-04-04 13:26:34.332529808 +0200
|
|
|
e0c9be |
@@ -137,6 +137,13 @@ ipAddressTable_container_init(netsnmp_co
|
|
|
e0c9be |
*container_ptr_ptr =
|
|
|
e0c9be |
netsnmp_container_find("ipAddressTable:table_container");
|
|
|
e0c9be |
if (NULL != *container_ptr_ptr) {
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * The container has ALLOW_DUPLICATES flag to speed up CONTAINER_INSERT
|
|
|
e0c9be |
+ * operations (it does not need to check for duplicates), however we
|
|
|
e0c9be |
+ * (manually) ensure that we won't insert any duplicates there.
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+ int rc;
|
|
|
e0c9be |
+ CONTAINER_SET_OPTIONS(*container_ptr_ptr, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
|
|
|
e0c9be |
(*container_ptr_ptr)->container_name = strdup("ipAddressTable");
|
|
|
e0c9be |
ipAddressTable_container_load(*container_ptr_ptr);
|
|
|
e0c9be |
CONTAINER_FOR_EACH(*container_ptr_ptr,
|
|
|
e0c9be |
@@ -205,8 +212,9 @@ static void
|
|
|
e0c9be |
_check_entry_for_updates(ipAddressTable_rowreq_ctx * rowreq_ctx,
|
|
|
e0c9be |
void **magic)
|
|
|
e0c9be |
{
|
|
|
e0c9be |
- netsnmp_container *ipaddress_container = (netsnmp_container*)magic[0];
|
|
|
e0c9be |
+ netsnmp_container *ipaddress_container = magic[0];
|
|
|
e0c9be |
netsnmp_container *to_delete = (netsnmp_container*)magic[1];
|
|
|
e0c9be |
+ netsnmp_container *to_ignore = (netsnmp_container *) magic[2];
|
|
|
e0c9be |
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
* check for matching entry using secondary index.
|
|
|
e0c9be |
@@ -234,10 +242,21 @@ _check_entry_for_updates(ipAddressTable_
|
|
|
e0c9be |
rowreq_ctx->ipAddressLastChanged = netsnmp_get_agent_uptime();
|
|
|
e0c9be |
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
- * remove entry from ifcontainer
|
|
|
e0c9be |
+ * Remember not to add this entry from 'ipaddress_container' to 'container' later.
|
|
|
e0c9be |
+ * Simple CONTAINER_REMOVE(ipaddress_container, ..) would be slow.
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
- CONTAINER_REMOVE(ipaddress_container, ipaddress_entry);
|
|
|
e0c9be |
- netsnmp_access_ipaddress_entry_free(ipaddress_entry);
|
|
|
e0c9be |
+ if (NULL == to_ignore) {
|
|
|
e0c9be |
+ magic[2] = to_ignore = netsnmp_container_find("access_ipaddress:table_container");
|
|
|
e0c9be |
+ if (NULL == to_ignore) {
|
|
|
e0c9be |
+ snmp_log(LOG_ERR, "couldn't create ignore container\n");
|
|
|
e0c9be |
+ } else {
|
|
|
e0c9be |
+ /* to speed up insertion */
|
|
|
e0c9be |
+ int rc;
|
|
|
e0c9be |
+ CONTAINER_SET_OPTIONS(to_ignore, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ if (NULL != to_ignore)
|
|
|
e0c9be |
+ CONTAINER_INSERT(to_ignore, ipaddress_entry);
|
|
|
e0c9be |
}
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
@@ -246,8 +265,11 @@ _check_entry_for_updates(ipAddressTable_
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
static void
|
|
|
e0c9be |
_add_new_entry(netsnmp_ipaddress_entry *ipaddress_entry,
|
|
|
e0c9be |
- netsnmp_container *container)
|
|
|
e0c9be |
+ void **magic)
|
|
|
e0c9be |
{
|
|
|
e0c9be |
+ netsnmp_container *container = magic[0];
|
|
|
e0c9be |
+ netsnmp_container *to_ignore = magic[2];
|
|
|
e0c9be |
+
|
|
|
e0c9be |
ipAddressTable_rowreq_ctx *rowreq_ctx;
|
|
|
e0c9be |
|
|
|
e0c9be |
DEBUGMSGTL(("ipAddressTable:access", "creating new entry\n"));
|
|
|
e0c9be |
@@ -255,6 +277,11 @@ _add_new_entry(netsnmp_ipaddress_entry *
|
|
|
e0c9be |
netsnmp_assert(NULL != ipaddress_entry);
|
|
|
e0c9be |
netsnmp_assert(NULL != container);
|
|
|
e0c9be |
|
|
|
e0c9be |
+ if (to_ignore && CONTAINER_FIND(to_ignore, ipaddress_entry)) {
|
|
|
e0c9be |
+ /* this entry already is in 'container', skip it */
|
|
|
e0c9be |
+ return;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
* allocate an row context and set the index(es)
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
@@ -329,36 +356,44 @@ int
|
|
|
e0c9be |
ipAddressTable_container_load(netsnmp_container *container)
|
|
|
e0c9be |
{
|
|
|
e0c9be |
netsnmp_container *ipaddress_container;
|
|
|
e0c9be |
- void *tmp_ptr[2];
|
|
|
e0c9be |
+ void *tmp_ptr[3];
|
|
|
e0c9be |
|
|
|
e0c9be |
DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_cache_load",
|
|
|
e0c9be |
"called\n"));
|
|
|
e0c9be |
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
- * TODO:351:M: |-> Load/update data in the ipAddressTable container.
|
|
|
e0c9be |
+ * Load/update data in the ipAddressTable container.
|
|
|
e0c9be |
* loop over your ipAddressTable data, allocate a rowreq context,
|
|
|
e0c9be |
* set the index(es) [and data, optionally] and insert into
|
|
|
e0c9be |
* the container.
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * netsnmp_access_ipaddress_container_load makes sure that
|
|
|
e0c9be |
+ * ipaddress_container does not contain any duplicate entries,
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+
|
|
|
e0c9be |
ipaddress_container =
|
|
|
e0c9be |
netsnmp_access_ipaddress_container_load(NULL,
|
|
|
e0c9be |
NETSNMP_ACCESS_IPADDRESS_LOAD_ADDL_IDX_BY_ADDR);
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
* we just got a fresh copy of interface data. compare it to
|
|
|
e0c9be |
* what we've already got, and make any adjustments, saving
|
|
|
e0c9be |
- * missing addresses to be deleted.
|
|
|
e0c9be |
+ * missing addresses to be deleted. Also, prune interfaces in
|
|
|
e0c9be |
+ * ipaddress_container, so only the new interfaces remain.
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
tmp_ptr[0] = ipaddress_container->next;
|
|
|
e0c9be |
- tmp_ptr[1] = NULL;
|
|
|
e0c9be |
+ tmp_ptr[1] = NULL; /* list of interfaces to be removed from 'container' */
|
|
|
e0c9be |
+ tmp_ptr[2] = NULL; /* list of interfaces to be ignored in ipaddress_container */
|
|
|
e0c9be |
CONTAINER_FOR_EACH(container, (netsnmp_container_obj_func *)
|
|
|
e0c9be |
_check_entry_for_updates, tmp_ptr);
|
|
|
e0c9be |
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
* now add any new interfaces
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
+ tmp_ptr[0] = container;
|
|
|
e0c9be |
CONTAINER_FOR_EACH(ipaddress_container,
|
|
|
e0c9be |
(netsnmp_container_obj_func *) _add_new_entry,
|
|
|
e0c9be |
- container);
|
|
|
e0c9be |
+ tmp_ptr);
|
|
|
e0c9be |
|
|
|
e0c9be |
/*
|
|
|
e0c9be |
* free the container. we've either claimed each entry, or released it,
|
|
|
e0c9be |
@@ -396,6 +431,19 @@ ipAddressTable_container_load(netsnmp_co
|
|
|
e0c9be |
*/
|
|
|
e0c9be |
CONTAINER_REMOVE(tmp_container, NULL);
|
|
|
e0c9be |
}
|
|
|
e0c9be |
+ CONTAINER_FREE(tmp_container);
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ if (NULL != tmp_ptr[2]) {
|
|
|
e0c9be |
+ /* list of interfaces to be ignored in ipaddress_container - free it */
|
|
|
e0c9be |
+ netsnmp_container *to_ignore = (netsnmp_container *) tmp_ptr[2];
|
|
|
e0c9be |
+ netsnmp_ipaddress_entry *ipaddress_entry;
|
|
|
e0c9be |
+ while (CONTAINER_SIZE(to_ignore)) {
|
|
|
e0c9be |
+ ipaddress_entry = (netsnmp_ipaddress_entry*)CONTAINER_FIRST(to_ignore);
|
|
|
e0c9be |
+ CONTAINER_REMOVE(to_ignore, ipaddress_entry);
|
|
|
e0c9be |
+ netsnmp_access_ipaddress_entry_free(ipaddress_entry);
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ CONTAINER_FREE(to_ignore);
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
DEBUGMSGT(("verbose:ipAddressTable:ipAddressTable_cache_load",
|
|
|
e0c9be |
diff -urNp old/agent/mibgroup/mibII/ipAddr.c new/agent/mibgroup/mibII/ipAddr.c
|
|
|
e0c9be |
--- old/agent/mibgroup/mibII/ipAddr.c 2012-10-10 00:28:58.000000000 +0200
|
|
|
e0c9be |
+++ new/agent/mibgroup/mibII/ipAddr.c 2017-04-04 13:28:56.547268946 +0200
|
|
|
e0c9be |
@@ -493,14 +493,16 @@ Address_Scan_Next(Index, Retin_ifaddr)
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
#elif defined(linux)
|
|
|
e0c9be |
+#include <errno.h>
|
|
|
e0c9be |
static struct ifreq *ifr;
|
|
|
e0c9be |
static int ifr_counter;
|
|
|
e0c9be |
|
|
|
e0c9be |
static void
|
|
|
e0c9be |
Address_Scan_Init(void)
|
|
|
e0c9be |
{
|
|
|
e0c9be |
- int num_interfaces = 0;
|
|
|
e0c9be |
+ int i;
|
|
|
e0c9be |
int fd;
|
|
|
e0c9be |
+ int lastlen = 0;
|
|
|
e0c9be |
|
|
|
e0c9be |
/* get info about all interfaces */
|
|
|
e0c9be |
|
|
|
e0c9be |
@@ -508,30 +510,45 @@ Address_Scan_Init(void)
|
|
|
e0c9be |
SNMP_FREE(ifc.ifc_buf);
|
|
|
e0c9be |
ifr_counter = 0;
|
|
|
e0c9be |
|
|
|
e0c9be |
- do
|
|
|
e0c9be |
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
|
e0c9be |
{
|
|
|
e0c9be |
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
|
e0c9be |
- {
|
|
|
e0c9be |
- DEBUGMSGTL(("snmpd", "socket open failure in Address_Scan_Init\n"));
|
|
|
e0c9be |
- return;
|
|
|
e0c9be |
- }
|
|
|
e0c9be |
- num_interfaces += 16;
|
|
|
e0c9be |
-
|
|
|
e0c9be |
- ifc.ifc_len = sizeof(struct ifreq) * num_interfaces;
|
|
|
e0c9be |
- ifc.ifc_buf = (char*) realloc(ifc.ifc_buf, ifc.ifc_len);
|
|
|
e0c9be |
-
|
|
|
e0c9be |
- if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
|
|
|
e0c9be |
- {
|
|
|
e0c9be |
- ifr=NULL;
|
|
|
e0c9be |
- close(fd);
|
|
|
e0c9be |
- return;
|
|
|
e0c9be |
- }
|
|
|
e0c9be |
- close(fd);
|
|
|
e0c9be |
+ DEBUGMSGTL(("snmpd", "socket open failure in Address_Scan_Init\n"));
|
|
|
e0c9be |
+ return;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * Cope with lots of interfaces and brokenness of ioctl SIOCGIFCONF
|
|
|
e0c9be |
+ * on some platforms; see W. R. Stevens, ``Unix Network Programming
|
|
|
e0c9be |
+ * Volume I'', p.435...
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ for (i = 8;; i *= 2) {
|
|
|
e0c9be |
+ ifc.ifc_len = sizeof(struct ifreq) * i;
|
|
|
e0c9be |
+ ifc.ifc_req = calloc(i, sizeof(struct ifreq));
|
|
|
e0c9be |
+
|
|
|
e0c9be |
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
|
|
|
e0c9be |
+ if (errno != EINVAL || lastlen != 0) {
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * Something has gone genuinely wrong...
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+ snmp_log(LOG_ERR, "bad rc from ioctl, errno %d", errno);
|
|
|
e0c9be |
+ SNMP_FREE(ifc.ifc_buf);
|
|
|
e0c9be |
+ close(fd);
|
|
|
e0c9be |
+ return;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ } else {
|
|
|
e0c9be |
+ if (ifc.ifc_len == lastlen) {
|
|
|
e0c9be |
+ /*
|
|
|
e0c9be |
+ * The length is the same as the last time; we're done...
|
|
|
e0c9be |
+ */
|
|
|
e0c9be |
+ break;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ lastlen = ifc.ifc_len;
|
|
|
e0c9be |
+ }
|
|
|
e0c9be |
+ free(ifc.ifc_buf); /* no SNMP_FREE, getting ready to reassign */
|
|
|
e0c9be |
}
|
|
|
e0c9be |
- while (ifc.ifc_len >= (sizeof(struct ifreq) * num_interfaces));
|
|
|
e0c9be |
-
|
|
|
e0c9be |
- ifr = ifc.ifc_req;
|
|
|
e0c9be |
close(fd);
|
|
|
e0c9be |
+ ifr = ifc.ifc_req;
|
|
|
e0c9be |
}
|
|
|
e0c9be |
|
|
|
e0c9be |
/*
|