summaryrefslogtreecommitdiffstats
path: root/main/kamailio
diff options
context:
space:
mode:
authorLeonardo Arena <rnalrd@alpinelinux.org>2015-02-24 14:19:05 +0000
committerLeonardo Arena <rnalrd@alpinelinux.org>2015-02-24 14:19:23 +0000
commit2bf3eba2ae475597bb7f20748e76b395bd68bbcd (patch)
tree89dc42297582aea0b36671ad9c947ce4cd06fcc7 /main/kamailio
parente211cfc6cb2183dafc51fd8a6a0562cdf4ce7612 (diff)
downloadaports-2bf3eba2ae475597bb7f20748e76b395bd68bbcd.tar.bz2
aports-2bf3eba2ae475597bb7f20748e76b395bd68bbcd.tar.xz
main/kamailio: add srv_query function to ipops module
Diffstat (limited to 'main/kamailio')
-rw-r--r--main/kamailio/APKBUILD7
-rw-r--r--main/kamailio/kamailio-4.2-ipops-srv-query.patch658
2 files changed, 664 insertions, 1 deletions
diff --git a/main/kamailio/APKBUILD b/main/kamailio/APKBUILD
index 5498eb7ae..a5802c2d1 100644
--- a/main/kamailio/APKBUILD
+++ b/main/kamailio/APKBUILD
@@ -13,7 +13,7 @@ _gittag=HEAD
pkgver=4.2.3
-pkgrel=0
+pkgrel=1
[ -z "${_gitcommit}" ] && _suffix="_src" || _suffix="-${_gitcommit}"
pkgdesc="Open Source SIP Server"
@@ -225,6 +225,8 @@ done
source="http://www.kamailio.org/pub/kamailio/$pkgver/src/kamailio-${pkgver}${_suffix}.tar.gz
kamailio-4.2-backslash.patch
0001-musl-fixes.patch
+ kamailio-4.2-ipops-srv-query.patch
+
kamailio.cfg
kamailio.initd
"
@@ -489,15 +491,18 @@ redis() {
md5sums="f94eb1db3820dba22bd3fdae464e93b3 kamailio-4.2.3_src.tar.gz
bad1ac2d4c95043df271d2ea6d37627a kamailio-4.2-backslash.patch
4685288dc54680597b00f956dc95d4d6 0001-musl-fixes.patch
+5b7ecf5c4ae06420c028e03721cb9e89 kamailio-4.2-ipops-srv-query.patch
a3c959ec568c43a905710e7d25cd8c25 kamailio.cfg
0e0a271fd3ddb7e87c01c26c7d041d59 kamailio.initd"
sha256sums="7dbbca4a515778d3e903380adcc49f727ddc4853238cb905e14c811a5671ed80 kamailio-4.2.3_src.tar.gz
d7e59be721ed0ad4621d404493b9a519708d801e9d4914b0164b819fa1abcd13 kamailio-4.2-backslash.patch
b98555ff304b51b82c6cf7e01d757b15ea4f05bd2e603c84d4384df6a6be62b6 0001-musl-fixes.patch
+cfe645fc80eaed8a9e4bd56047f75555b2a9e3edcb3e2b6c6cece1547ab0a574 kamailio-4.2-ipops-srv-query.patch
8024266849033a917147827c3579a382f10f3796989bebc6de3d7c80c965fb72 kamailio.cfg
a90d3ab09a3ed58892e94710a1f80492a61ffad1ccf7ccb5b851bb8f538d32c4 kamailio.initd"
sha512sums="2f42499fe84eefac236fe3d4aa3c7bc424944236f00b95a7071feaa816b3df5764f84076d57b2137908dab7ff06a2440cc7a53a799216befd9511f8718a2eee5 kamailio-4.2.3_src.tar.gz
a9bb1e8f9f373264b8351ddae099a36a46ddd46fdec09e468d297ba4f64bb4896e7d6e599da70a424e8a28695ab3f3b4ac940afab534593a6b9d08ae462f001a kamailio-4.2-backslash.patch
dea7ef2ccf01357576045ba375d41301e2447b4454324007c7ca1862322835c57045852017192ca5434b32dd1b7a2e9669209b7111889dab335b74f042d0f11f 0001-musl-fixes.patch
+3a9bb5d05b4628f6146b824b8916db259f1da51415398ba420900311d73986d21cab653079080c0e8c55ef512909f542279ca7da944b5ef14520331584ca958f kamailio-4.2-ipops-srv-query.patch
0b666bfa10fd0af97b62749f8691cb3f76d9b40d1abe0a33e810e367bd733d2e8189c89f7f23010ec591116aada6e1a8a403b17449fe775038917617f281ad4d kamailio.cfg
5ddaa059cdef10462c904f061f7bb085e62ad7501e2ed41f797d9e68822bce4e0e5ca09c1586c3901c920f8ce563c8c3ede860752c2b9bdb8f09908388ef337f kamailio.initd"
diff --git a/main/kamailio/kamailio-4.2-ipops-srv-query.patch b/main/kamailio/kamailio-4.2-ipops-srv-query.patch
new file mode 100644
index 000000000..450215e11
--- /dev/null
+++ b/main/kamailio/kamailio-4.2-ipops-srv-query.patch
@@ -0,0 +1,658 @@
+--- a/modules/ipops/ipops_mod.c
++++ b/modules/ipops/ipops_mod.c
+@@ -21,6 +21,7 @@
+ *
+ * History:
+ * -------
++ * 2015-02-04: Added srv_query function (rboisvert)
+ * 2011-07-29: Added a function to detect RFC1918 private IPv4 addresses (ibc)
+ * 2011-04-27: Initial version (ibc)
+ */
+@@ -92,10 +93,13 @@
+ static int w_dns_int_match_ip(sip_msg_t*, char*, char*);
+
+ static int w_dns_query(struct sip_msg* msg, char* str1, char* str2);
++static int w_srv_query(struct sip_msg* msg, char* str1, char* str2);
+
+ static pv_export_t mod_pvs[] = {
+ { {"dns", sizeof("dns")-1}, PVT_OTHER, pv_get_dns, 0,
+ pv_parse_dns_name, 0, 0, 0 },
++ { {"srvquery", sizeof("srvquery")-1}, PVT_OTHER, pv_get_srv, 0,
++ pv_parse_srv_name, 0, 0, 0 },
+ { {"HN", sizeof("HN")-1}, PVT_OTHER, pv_get_hn, 0,
+ pv_parse_hn_name, 0, 0, 0 },
+ { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+@@ -132,6 +136,8 @@
+ ANY_ROUTE },
+ { "dns_query", (cmd_function)w_dns_query, 2, fixup_spve_spve, 0,
+ ANY_ROUTE },
++ { "srv_query", (cmd_function)w_srv_query, 2, fixup_spve_spve, 0,
++ ANY_ROUTE },
+ { "bind_ipops", (cmd_function)bind_ipops, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0 }
+ };
+@@ -772,4 +778,32 @@
+ }
+
+ return dns_update_pv(&hostname, &name);
++}
++
++/**
++ *
++ */
++static int w_srv_query(struct sip_msg* msg, char* str1, char* str2)
++{
++ str srvcname;
++ str name;
++
++ if(msg==NULL)
++ {
++ LM_ERR("received null msg\n");
++ return -1;
++ }
++
++ if(fixup_get_svalue(msg, (gparam_t*)str1, &srvcname)<0)
++ {
++ LM_ERR("cannot get the srvcname\n");
++ return -1;
++ }
++ if(fixup_get_svalue(msg, (gparam_t*)str2, &name)<0)
++ {
++ LM_ERR("cannot get the pvid name\n");
++ return -1;
++ }
++
++ return srv_update_pv(&srvcname, &name);
+ }
+--- a/modules/ipops/ipops_pv.c
++++ b/modules/ipops/ipops_pv.c
+@@ -32,6 +32,7 @@
+ #include <netinet/in.h>
+
+ #include "../../dprint.h"
++#include "../../rand/fastrand.h"
+ #include "../../hashes.h"
+ #include "../../resolve.h"
+ #include "../../pvar.h"
+@@ -66,7 +67,6 @@
+ int nidx;
+ } dns_pv_t;
+
+-
+ static sr_dns_item_t *_sr_dns_list = NULL;
+
+ /**
+@@ -132,7 +132,6 @@
+ return it;
+ }
+
+-
+ /**
+ *
+ */
+@@ -431,7 +430,6 @@
+ return 1;
+ }
+
+-
+ struct _hn_pv_data {
+ str data;
+ str fullname;
+@@ -594,4 +592,545 @@
+ return pv_get_null(msg, param, res);;
+ return pv_get_strval(msg, param, res, &_hn_data->hostname);
+ }
++}
++
++/**********
++* srvquery PV
++**********/
++
++static char *srvqrylst []
++ = {"count", "port", "priority", "target", "weight", NULL};
++
++#define PV_SRV_MAXSTR 64
++#define PV_SRV_MAXRECS 32
++
++typedef struct _sr_srv_record {
++ unsigned short priority;
++ unsigned short weight;
++ unsigned short port;
++ char target [PV_SRV_MAXSTR + 1];
++} sr_srv_record_t;
++
++typedef struct _sr_srv_item {
++ str pvid;
++ unsigned int hashid;
++ int count;
++ sr_srv_record_t rr [PV_SRV_MAXRECS];
++ struct _sr_srv_item *next;
++} sr_srv_item_t;
++
++typedef struct _srv_pv {
++ sr_srv_item_t *item;
++ int type;
++ int flags;
++ pv_spec_t *pidx;
++ int nidx;
++} srv_pv_t;
++
++static sr_srv_item_t *_sr_srv_list = NULL;
++
++/**********
++* Add srvquery Item
++*
++* INPUT:
++* Arg (1) = pvid string pointer
++* Arg (2) = find flag; <>0=search only
++* OUTPUT: srv record pointer; NULL=not found
++**********/
++
++sr_srv_item_t *sr_srv_add_item (str *pvid, int findflg)
++
++{
++sr_srv_item_t *pitem;
++unsigned int hashid;
++
++/**********
++* o get hash
++* o already exists?
++**********/
++
++hashid = get_hash1_raw (pvid->s, pvid->len);
++for (pitem = _sr_srv_list; pitem; pitem = pitem->next) {
++ if (pitem->hashid == hashid
++ && pitem->pvid.len == pvid->len
++ && !strncmp (pitem->pvid.s, pvid->s, pvid->len))
++ return pitem;
++}
++if (findflg)
++ return NULL;
++
++/**********
++* o alloc/init item structure
++* o link in new item
++**********/
++
++pitem = (sr_srv_item_t *) pkg_malloc (sizeof (sr_srv_item_t));
++if (!pitem) {
++ LM_ERR ("No more pkg memory!\n");
++ return NULL;
++}
++memset (pitem, 0, sizeof (sr_srv_item_t));
++pitem->pvid.s = (char *) pkg_malloc (pvid->len + 1);
++if (!pitem->pvid.s) {
++ LM_ERR ("No more pkg memory!\n");
++ pkg_free (pitem);
++ return NULL;
++}
++memcpy (pitem->pvid.s, pvid->s, pvid->len);
++pitem->pvid.len = pvid->len;
++pitem->hashid = hashid;
++pitem->next = _sr_srv_list;
++_sr_srv_list = pitem;
++return pitem;
++}
++
++/**********
++* Skip Over
++*
++* INPUT:
++* Arg (1) = string pointer
++* Arg (2) = starting position
++* Arg (3) = whitespace flag
++* OUTPUT: position past skipped
++**********/
++
++int skip_over (str *pstr, int pos, int bWS)
++
++{
++char *pchar;
++
++/**********
++* o string exists?
++* o skip over
++**********/
++
++if (pos >= pstr->len)
++ return pstr->len;
++for (pchar = &pstr->s [pos]; pos < pstr->len; pchar++, pos++) {
++ if (*pchar == ' ' || *pchar == '\t' || *pchar == '\n' || *pchar == '\r') {
++ if (bWS)
++ continue;
++ }
++ if ((*pchar>='A' && *pchar<='Z') || (*pchar>='a' && *pchar<='z')
++ || (*pchar>='0' && *pchar<='9')) {
++ if (!bWS)
++ continue;
++ }
++ break;
++}
++return pos;
++}
++
++/**********
++* Sort SRV Records by Weight (RFC 2782)
++*
++* INPUT:
++* Arg (1) = pointer to array of SRV records
++* Arg (2) = first record in range
++* Arg (3) = last record in range
++* OUTPUT: position past skipped
++**********/
++
++void sort_weights (struct srv_rdata **plist, int pos1, int pos2)
++
++{
++int idx1, idx2, idx3, lastfound;
++struct srv_rdata *wlist [PV_SRV_MAXRECS];
++unsigned int rand, sum, sums [PV_SRV_MAXRECS];
++
++/**********
++* place zero weights in the unordered list and then non-zero
++**********/
++
++idx2 = 0;
++for (idx1 = pos1; idx1 <= pos2; idx1++) {
++ if (!plist [idx1]->weight) {
++ wlist [idx2++] = plist [idx1];
++ }
++}
++for (idx1 = pos1; idx1 <= pos2; idx1++) {
++ if (plist [idx1]->weight) {
++ wlist [idx2++] = plist [idx1];
++ }
++}
++
++/**********
++* generate running sum list
++**********/
++
++sum = 0;
++for (idx1 = pos1; idx1 <= pos2; idx1++) {
++ sum += wlist [idx1]->weight;
++ sums [pos1 - idx1] = sum;
++}
++
++/**********
++* resort randomly
++**********/
++
++idx3 = pos1;
++lastfound = 0;
++for (idx1 = pos2 - pos1; idx1; --idx1) {
++ /**********
++ * o calculate a random number in range
++ * o find first unsorted
++ **********/
++
++ rand = fastrand_max (sum);
++ for (idx2 = 0; idx2 < pos2 - pos1; idx2++) {
++ if (!wlist [idx2]) {
++ continue;
++ }
++ if (sums [idx2] >= rand) {
++ plist [idx3++] = wlist [idx2];
++ wlist [idx2] = 0;
++ break;
++ }
++ lastfound = idx2;
++ }
++ if (idx2 == pos2 - pos1) {
++ plist [idx3++] = wlist [lastfound];
++ wlist [lastfound] = 0;
++ }
++}
++return;
++}
++
++/**********
++* Sort SRV Records by Priority/Weight
++*
++* INPUT:
++* Arg (1) = pointer to array of SRV records
++* Arg (2) = record count
++* OUTPUT: position past skipped
++**********/
++
++void sort_srv (struct srv_rdata **plist, int rcount)
++
++{
++int idx1, idx2;
++struct srv_rdata *pswap;
++
++/**********
++* sort by priority
++**********/
++
++for (idx1 = 1; idx1 < rcount; idx1++) {
++ pswap = plist [idx1];
++ for (idx2 = idx1;
++ idx2 && (plist [idx2 - 1]->priority > pswap->priority); --idx2) {
++ plist [idx2] = plist [idx2 - 1];
++ }
++ plist [idx2] = pswap;
++}
++
++/**********
++* check for multiple priority
++**********/
++
++idx2 = 0;
++pswap = plist [0];
++for (idx1 = 1; idx1 <= rcount; idx1++) {
++ if ((idx1 == rcount) || (pswap->priority != plist [idx1]->priority)) {
++ /**********
++ * o range has more than one element?
++ * o restart range
++ **********/
++
++ if (idx1 - idx2 - 1) {
++ sort_weights (plist, idx2, idx1 - 1);
++ }
++ idx2 = idx1;
++ pswap = plist [idx2];
++ }
++}
++return;
++}
++
++/**********
++* Parse srvquery Name
++*
++* INPUT:
++* Arg (1) = pv spec pointer
++* Arg (2) = input string pointer
++* OUTPUT: 0=success
++**********/
++
++int pv_parse_srv_name (pv_spec_t *sp, str *in)
++
++{
++char *pstr;
++int i, pos, sign;
++srv_pv_t *dpv;
++str pvi, pvk, pvn;
++
++/**********
++* o alloc/init pvid structure
++* o extract pvid name
++* o check separator
++**********/
++
++if (!sp || !in || in->len<=0)
++ return -1;
++dpv = (srv_pv_t *) pkg_malloc (sizeof (srv_pv_t));
++if (!dpv) {
++ LM_ERR ("No more pkg memory!\n");
++ return -1;
++}
++memset (dpv, 0, sizeof (srv_pv_t));
++pos = skip_over (in, 0, 1);
++if (pos == in->len)
++ goto error;
++pvn.s = &in->s [pos];
++pvn.len = pos;
++pos = skip_over (in, pos, 0);
++pvn.len = pos - pvn.len;
++if (!pvn.len)
++ goto error;
++pos = skip_over (in, pos, 1);
++if ((pos + 2) > in->len)
++ goto error;
++if (strncmp (&in->s [pos], "=>", 2))
++ goto error;
++
++/**********
++* o extract key name
++* o check key name
++* o count?
++**********/
++
++pos = skip_over (in, pos + 2, 1);
++pvk.s = &in->s [pos];
++pvk.len = pos;
++pos = skip_over (in, pos, 0);
++pvk.len = pos - pvk.len;
++if (!pvk.len)
++ goto error;
++for (i = 0; srvqrylst [i]; i++) {
++ if (strlen (srvqrylst [i]) != pvk.len)
++ continue;
++ if (!strncmp (pvk.s, srvqrylst [i], pvk.len)) {
++ dpv->type = i;
++ break;
++ }
++}
++if (!srvqrylst [i])
++ goto error;
++if (!i)
++ goto noindex;
++
++/**********
++* o check for array
++* o extract array index and check
++**********/
++
++pos = skip_over (in, pos, 1);
++if ((pos + 3) > in->len)
++ goto error;
++if (in->s [pos] != '[')
++ goto error;
++pos = skip_over (in, pos + 1, 1);
++if ((pos + 2) > in->len)
++ goto error;
++pvi.s = &in->s [pos];
++pvi.len = pos;
++if (in->s [pos] == PV_MARKER) {
++ /**********
++ * o search from the end back to array close
++ * o get PV value
++ **********/
++
++ for (i = in->len - 1; i != pos; --i) {
++ if (in->s [i] == ']')
++ break;
++ }
++ if (i == pos)
++ goto error;
++ pvi.len = i - pvi.len;
++ pos = i + 1;
++ dpv->pidx = pv_cache_get (&pvi);
++ if (!dpv->pidx)
++ goto error;
++ dpv->flags |= SR_DNS_PVIDX;
++} else {
++ /**********
++ * o get index value
++ * o check for reverse index
++ * o convert string to number
++ **********/
++
++ pos = skip_over (in, pos, 0);
++ pvi.len = pos - pvi.len;
++ sign = 1;
++ i = 0;
++ pstr = pvi.s;
++ if (*pstr == '-') {
++ sign = -1;
++ i++;
++ pstr++;
++ }
++ for (dpv->nidx = 0; i < pvi.len; i++) {
++ if (*pstr >= '0' && *pstr <= '9')
++ dpv->nidx = (dpv->nidx * 10) + *pstr++ - '0';
++ }
++ if (i != pvi.len)
++ goto error;
++ dpv->nidx *= sign;
++ pos = skip_over (in, pos, 1);
++ if (pos == in->len)
++ goto error;
++ if (in->s [pos++] != ']')
++ goto error;
++}
++
++/**********
++* o check for trailing whitespace
++* o add data to PV
++**********/
++
++noindex:
++if (skip_over (in, pos, 1) != in->len)
++ goto error;
++LM_DBG ("srvquery (%.*s => %.*s [%.*s])\n",
++ pvn.len, pvn.s, pvk.len, pvk.s, pvi.len, pvi.s);
++dpv->item = sr_srv_add_item (&pvn, 0);
++if (!dpv->item)
++ goto error;
++sp->pvp.pvn.u.dname = (void *)dpv;
++sp->pvp.pvn.type = PV_NAME_OTHER;
++return 0;
++
++error:
++LM_ERR ("error at PV srvquery: %.*s@%d\n", in->len, in->s, pos);
++pkg_free (dpv);
++return -1;
++}
++
++int srv_update_pv (str *srvcname, str *pvid)
++
++{
++int idx1, idx2, rcount;
++struct rdata *phead, *psrv;
++struct srv_rdata *plist [PV_SRV_MAXRECS];
++sr_srv_item_t *pitem;
++sr_srv_record_t *prec;
++
++/**********
++* o service name missing?
++* o find pvid
++**********/
++
++if (!srvcname->len) {
++ LM_DBG ("service name missing: %.*s\n", srvcname->len, srvcname->s);
++ return -2;
++}
++pitem = sr_srv_add_item (pvid, 1);
++if (!pitem) {
++ LM_DBG ("pvid not found: %.*s\n", pvid->len, pvid->s);
++ return -3;
++}
++
++/**********
++* o get records
++* o sort by priority/weight
++* o save to PV
++**********/
++
++LM_DBG ("attempting to query: %.*s\n", srvcname->len, srvcname->s);
++phead = get_record (srvcname->s, T_SRV, RES_ONLY_TYPE);
++rcount = 0;
++for (psrv = phead; psrv; psrv = psrv->next) {
++ if (rcount < PV_SRV_MAXRECS) {
++ plist [rcount++] = (struct srv_rdata *) psrv->rdata;
++ } else {
++ LM_WARN ("truncating srv_query list to %d records!", PV_SRV_MAXRECS);
++ break;
++ }
++}
++pitem->count = rcount;
++if (rcount)
++ sort_srv (plist, rcount);
++for (idx1 = 0; idx1 < rcount; idx1++) {
++ prec = &pitem->rr [idx1];
++ prec->priority = plist [idx1]->priority;
++ prec->weight = plist [idx1]->weight;
++ prec->port = plist [idx1]->port;
++ idx2 = plist [idx1]->name_len;
++ if (idx2 > PV_SRV_MAXSTR) {
++ LM_WARN ("truncating srv_query target (%.*s)!", idx2, plist [idx1]->name);
++ idx2 = PV_SRV_MAXSTR;
++ }
++ strncpy (prec->target, plist [idx1]->name, idx2);
++ prec->target [idx2] = '\0';
++}
++if (phead)
++ free_rdata_list (phead);
++LM_DBG ("srvquery PV updated for: %.*s (%d)\n",
++ srvcname->len, srvcname->s, rcount);
++return 1;
++}
++
++/**********
++* Get srvquery Values
++*
++* INPUT:
++* Arg (1) = SIP message pointer
++* Arg (2) = parameter pointer
++* Arg (3) = PV value pointer
++* OUTPUT: 0=success
++**********/
++
++int pv_get_srv (sip_msg_t *pmsg, pv_param_t *param, pv_value_t *res)
++
++{
++pv_value_t val;
++srv_pv_t *dpv;
++
++/**********
++* o sipmsg and param exist?
++* o PV name exists?
++* o count?
++**********/
++
++if(!pmsg || !param)
++ return -1;
++dpv = (srv_pv_t *) param->pvn.u.dname;
++if(!dpv || !dpv->item)
++ return -1;
++if (!dpv->type)
++ return pv_get_sintval (pmsg, param, res, dpv->item->count);
++
++/**********
++* o get index value
++* o reverse index?
++* o extract data
++**********/
++
++if (!dpv->pidx) {
++ val.ri = dpv->nidx;
++} else {
++ if (pv_get_spec_value (pmsg, dpv->pidx, &val) < 0
++ || !(val.flags & PV_VAL_INT)) {
++ LM_ERR ("failed to evaluate index variable!\n");
++ return pv_get_null (pmsg, param, res);
++ }
++}
++if (val.ri < 0) {
++ if ((dpv->item->count + val.ri) < 0)
++ return pv_get_null (pmsg, param, res);
++ val.ri = dpv->item->count + val.ri;
++}
++if (val.ri >= dpv->item->count)
++ return pv_get_null(pmsg, param, res);
++switch (dpv->type) {
++ case 1: /* port */
++ return pv_get_sintval (pmsg, param, res, dpv->item->rr [val.ri].port);
++ case 2: /* priority */
++ return pv_get_sintval (pmsg, param, res, dpv->item->rr [val.ri].priority);
++ case 3: /* target */
++ return pv_get_strzval (pmsg, param, res, dpv->item->rr [val.ri].target);
++ case 4: /* weight */
++ return pv_get_sintval (pmsg, param, res, dpv->item->rr [val.ri].weight);
++}
++return pv_get_null (pmsg, param, res);
+ }
+--- a/modules/ipops/ipops_pv.h
++++ b/modules/ipops/ipops_pv.h
+@@ -39,5 +39,9 @@
+ int pv_get_hn(struct sip_msg *msg, pv_param_t *param,
+ pv_value_t *res);
+
++int pv_parse_srv_name(pv_spec_t *, str *);
++int pv_get_srv(sip_msg_t *, pv_param_t *, pv_value_t *);
++int srv_update_pv(str *, str *);
++
+ #endif
+