Browse Source

Parsing of select framework syntax moved to the select core; serdev doc showing how to extend select framework within module.

Michal Matyska 19 years ago
parent
commit
6f1faf761a
4 changed files with 351 additions and 0 deletions
  1. 223 0
      doc/serdev/select_module.xml
  2. 2 0
      doc/serdev/serdev.xml
  3. 103 0
      select.c
  4. 23 0
      select.h

+ 223 - 0
doc/serdev/select_module.xml

@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="select_module" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+    
+<section><title>Extend select framework with module related functions</title>
+<para>
+	If you want to extend functions which select framework can call with some
+	dependent on module you have to follow next steps:
+	<itemizedlist>
+		<listitem><para>
+			define working functions
+			</para>
+		</listitem>
+		<listitem><para>
+			create module's select parsing table
+		</para>
+		</listitem>
+		<listitem><para>
+			in module's mod_init call register_select_table
+		</para>
+		</listitem>
+	</itemizedlist>
+</para>
+</section>
+<section>
+<title>Define your working functions</title>
+<para>
+	Working functions are the piece of code, which is called as when SER needs
+	get result of select function (defined as @name.foo[2].bar[5]). The working copy
+	has following definition:
+</para>
+<para><emphasis>
+int <function>select_function_name</function>(str* res, struct select *s, struct sip_msg *msg)
+</emphasis>
+	Pointer to the string result workspace <emphasis>res</emphasis>, don't allocate
+	memory for the string, but use static buffer. There is no way to free the
+	allocated memory.
+	Pointer to the parsed select structure <emphasis>s</emphasis>. Holds all names,
+	numbers divided by the dot and square bracket notation. Use that if any of the
+	part used CONSUME_NEXT_STR or CONSUME_NEXT_INT to get the value.
+	Pointer to the processed message structure <emphasis>msg</emphasis>.
+</para>
+<para>
+	<emphasis>FIXUP CALL:</emphasis>
+	If you set FIXUP_CALL flag for the final
+	function, the fixup call will be done immediatelly after function resolution.
+	Such call is indicated with res==NULL &amp;&amp; msg==NULL. Such call can convert any of
+	the select's parameter into internal data (can hold one pointer), if you do
+	that, set param_type to SEL_PARAM_DATA.
+</para>
+<para>
+	Result code of the function declares if the call was sucessful and if the result is
+	valid string or empty string.
+</para>
+<itemizedlist>
+	<listitem><para><emphasis>-1</emphasis> error</para></listitem>
+	<listitem><para><emphasis>0</emphasis> success, res contains non-empty string</para></listitem>
+	<listitem><para><emphasis>1</emphasis> success, result of select call is empty string (res can be left
+	unchanged)</para></listitem>
+</itemizedlist>
+</section>
+<section>
+<title>Create module's select parsing table</title>
+<para>Define static table of select_row_t type and initialize it directly.
+</para>
+<para>The table is representation of tree (or oriented graph if you want), where
+first column represents current (up-to now) resolved function (starting with NULL),
+next two columns define if next parameter is integer or particullar string, next
+column is new resolved function which will be tested in next round and the last
+column is set of flags.
+</para>
+<programlisting><![CDATA[
+static select_row_t test_sel[] = {
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("test"), select_test, 0},
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("value"), select_test_val, 0},
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("echo"), select_test_echo, CONSUME_NEXT_STR},
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
+};
+]]>
+</programlisting>
+<para>
+So in the previous example, the first line will accept syntax
+<emphasis>@test</emphasis> and set the resolved function to select_test. In the
+next round, all rows with the select_test in the first column will be used to
+match, so the next two lines
+are candidates to match depending on the next part of select syntax. If it
+matches <emphasis>@test.value</emphasis>, the function is resolved to
+select_test_val, if it matches <emphasis>@test.echo.anystring</emphasis>, it is
+resolved into select_test_echo. Flag <emphasis>CONSUME_NEXT_STR</emphasis> will
+accept any string at the 3rd position. As the <emphasis>OPTIONAL</emphasis> flag
+is not present, it won't accept just the <emphasis>@test.echo</emphasis> syntax.
+</para>
+<para>
+The table ends with the <emphasis>NULL</emphasis> in the 1st and 4th column,
+other columns are not checked (the notation in the example suits well).
+</para>
+<para>
+At the resolving time all function names must be already defined. For functions which are
+not leaves, you can use macro <emphasis>ABSTRACT_F(name)</emphasis> to define empty
+function, for working function you can use advance definition using
+<emphasis>SELECT_F(name)</emphasis> macro.
+</para>
+</section>
+<!--
+TODO:
+<section>
+<title>Available flag options and their meaning</title>
+<para>
+<emphasis></emphasis>
+</para>
+</section>
+-->
+<section>
+<title>Register the created table</title>
+<para>
+	In the module initialization function call <emphasis>register_select_table(table)</emphasis>
+	where table is the parsing tree/table you have defined in previous step.
+	This call ensures, that the table will become part of the parsing logic for
+	all select framework calls defined in the script file or called by another
+	module's parse_select.
+</para>
+<para>
+	<emphasis>NOTE:</emphasis> The tables are inserted into the beginning of the
+	list, so the core's table (and thus parseable names and definitions) can be
+	overrided by module's function, if it is defined with the same name. To
+	avoid such situation, the best practice is to start module's select with the
+	module's name. E.g in our example code both select functions start
+	with @test...
+</para>
+</section>
+<section>
+<title>Example - module defining select extension</title>
+<para>Example module <emphasis>test</emphasis>, which defines two select function.
+<itemizedlist>
+	<listitem><para>
+	<emphasis>@test.value</emphasis> - returns value passed as modules parameter
+	"value"
+	</para></listitem>
+	<listitem><para>
+	<emphasis>@test.echo.xxx</emphasis> - returns xxx regardless of what you put
+	as xxx (shows CONSUME_NEXT_STR option)
+	</para></listitem>
+</itemizedlist>
+</para>
+<example id="test.c">
+<title>test.c</title>
+<programlisting><![CDATA[
+#include <string.h>
+#include "../../sr_module.h"
+#include "../../str.h"
+#include "../../dprint.h"
+#include "../../select.h"
+
+MODULE_VERSION
+
+static cmd_export_t cmds[] = {
+	{0, 0, 0, 0, 0}
+};
+
+static char* value=NULL;
+
+static param_export_t params[] = {
+	{"value", STR_PARAM, &value},
+	{0, 0, 0}
+};
+
+int select_test_val(str* res, struct select* s, struct sip_msg* msg) {
+	DBG("SELECT_TEST_VAL called test_val=%s\n", value);
+	res->s=value;
+	res->len=strlen(value);
+	return 0;
+}
+
+int select_test_echo(str* res, struct select* s, struct sip_msg* msg) {
+	DBG("SELECT_TEST_ECHO called\n");
+	if (s->params[s->n-1].type==SEL_PARAM_STR) {
+		*res=s->params[s->n-1].v.s;
+		return 0;
+	} else
+		return -1;
+}
+
+ABSTRACT_F(select_test)
+	
+static select_row_t test_sel[] = {
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("test"), select_test, 0},
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("value"), select_test_val, 0},
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("echo"), select_test_echo, CONSUME_NEXT_STR},
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
+};
+ 
+static int init(void) {
+	register_select_table(test_sel);
+	return 0;
+};
+
+struct module_exports exports = {
+	"test", 
+	cmds,           /* Exported commands */
+	0,              /* RPC methods */
+	params,         /* Exported parameters */
+	init,           /* module initialization function */
+	0,              /* response function*/
+	0,              /* destroy function */
+	0,              /* oncancel function */
+	0               /* per-child init function */
+};
+
+]]>
+</programlisting>
+</example>
+</section>
+</section>

+ 2 - 0
doc/serdev/serdev.xml

@@ -61,6 +61,8 @@
     <xi:include href="db_interface.xml"/>
     <xi:include href="locking.xml"/>
 
+	<xi:include href="select_module.xml"/>
+
     <!--    
     TODO:
     * How to traverse all headers of given type

+ 103 - 0
select.c

@@ -31,14 +31,22 @@
  *              DIVERSION flag checked
  *  2006-02-26  don't free str when changing type STR -> DIVERSION (mma)
  *				it can't be freeable sometimes (e.g. xlog's select)
+ *	2006-05-30  parse_select (mma)
+ *	2006-06-02  shm_parse_select (mma)
  *
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
 
 #include "select.h"
 #include "dprint.h"
 #include "select_core.h"
 #include "mem/mem.h"
+#include "mem/shm_mem.h"
 
 /*
  * The main parser table list placeholder
@@ -47,6 +55,101 @@
  */
 static select_table_t *select_list = &select_core_table;
 
+/*
+ * Parse select string into select structure s
+ * moves pointer p to the first unused char
+ *
+ * Returns -1 error
+ *			  p points to the first unconsumed char
+ *          0 success
+ *			  p points to the first unconsumed char
+ *			  s points to the select structure
+ */
+
+int w_parse_select(char**p, select_t* sel)
+{
+	str name;
+	
+	if (**p=='@') (*p)++;
+	sel->n=0;
+	while (isalpha(*(*p))) {
+		if (sel->n > MAX_SELECT_PARAMS -2) {
+			ERR("parse_select: select depth exceeds max\n");
+			goto error;
+		}
+		name.s=(*p);
+		while (isalpha(*(*p)) || isdigit(*(*p)) || (*(*p)=='_')) (*p)++;
+		name.len=(*p)-name.s;
+		sel->params[sel->n].type=SEL_PARAM_STR;
+		sel->params[sel->n].v.s=name;
+		DBG("parse_select: part %d: %.*s\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
+		sel->n++;
+		if (*(*p)=='[') {
+			(*p)++; 
+			name.s=(*p);
+			if (*(*p)=='-') (*p)++;
+			while (isdigit(*(*p))) (*p)++;
+			name.len=(*p)-name.s;
+			if (*(*p)!=']') {
+				ERR("parse_select: invalid index, no closing ]\n");
+				goto error;
+			};
+			(*p)++;
+			sel->params[sel->n].type=SEL_PARAM_INT;
+			sel->params[sel->n].v.i=atoi(name.s);
+			DBG("parse_select: part %d: [%d]\n", sel->n, sel->params[sel->n].v.i);
+			sel->n++;
+		}
+		if (*(*p)!='.') break;
+		(*p)++;
+	};
+	if (sel->n==0) {
+		ERR("parse_select: invalid select\n");
+		goto error;
+	};
+	DBG("parse_select: end, total elements: %d, calling resolve_select\n", sel->n);
+	if (resolve_select(sel)<0) {
+		ERR("parse_select: error while resolve_select\n");
+		goto error;
+	}
+	return 0;
+	
+error:
+	return -1;
+}
+
+int parse_select (char** p, select_t** s)
+{
+	select_t* sel;
+	
+	sel=(select_t*)pkg_malloc(sizeof(select_t));
+	if (!sel) {
+		ERR("parse_select: no free memory\n");
+		return -1;
+	}
+	if (w_parse_select(p, sel)<0) {
+		return -2;
+	}
+	*s=sel;
+	return 0;
+}
+
+int shm_parse_select (char** p, select_t** s)
+{
+	select_t* sel;
+	
+	sel=(select_t*)shm_malloc(sizeof(select_t));
+	if (!sel) {
+		ERR("parse_select: no free shared memory\n");
+		return -1;
+	}
+	if (w_parse_select(p, sel)<0) {
+		return -2;
+	}
+	*s=sel;
+	return 0;
+}
+
 int resolve_select(select_t* s)
 {
 	select_f f;

+ 23 - 0
select.h

@@ -157,6 +157,29 @@ void print_select(select_t* s);
  */
 int register_select_table(select_row_t *table);
 
+/*
+ * Tries to parse string pointed by *p (up to first non alpha char) into select structure
+ * if parsing succeeded, call resolve_select
+ * if resolving passes, returns final structure
+ * *p moves to first unused char
+ * return 0
+ *
+ * if memory allocation fails, returns -1
+ * if parsing or resolving fails, returns -2
+ */
+int parse_select (char** p, select_t** s);
+
+/*
+ * Select parser, result is stored in SHARED memory
+ * 
+ * If you call this, you must ensure, that the string which
+ * is beeing parsed MUST be at the same place for all child
+ * processes, e.g. allocated in the shared memory as well
+ *
+ * parameters and results same as parse_select
+ */
+int shm_parse_select (char** p, select_t** s);
+
 #define SELECT_F(function) extern int function (str* res, select_t* s, struct sip_msg* msg);
 #define ABSTRACT_F(function) int function (str* res, select_t* s, struct sip_msg* msg) {return -1;}