Browse code

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

Michal Matyska authored on 09/06/2006 16:46:53
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,223 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
3
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
4
+
5
+<section id="select_module" xmlns:xi="http://www.w3.org/2001/XInclude">
6
+    <sectioninfo>
7
+	<revhistory>
8
+	    <revision>
9
+		<revnumber>$Revision$</revnumber>
10
+		<date>$Date$</date>
11
+	    </revision>
12
+	</revhistory>
13
+    </sectioninfo>
14
+    
15
+<section><title>Extend select framework with module related functions</title>
16
+<para>
17
+	If you want to extend functions which select framework can call with some
18
+	dependent on module you have to follow next steps:
19
+	<itemizedlist>
20
+		<listitem><para>
21
+			define working functions
22
+			</para>
23
+		</listitem>
24
+		<listitem><para>
25
+			create module's select parsing table
26
+		</para>
27
+		</listitem>
28
+		<listitem><para>
29
+			in module's mod_init call register_select_table
30
+		</para>
31
+		</listitem>
32
+	</itemizedlist>
33
+</para>
34
+</section>
35
+<section>
36
+<title>Define your working functions</title>
37
+<para>
38
+	Working functions are the piece of code, which is called as when SER needs
39
+	get result of select function (defined as @name.foo[2].bar[5]). The working copy
40
+	has following definition:
41
+</para>
42
+<para><emphasis>
43
+int <function>select_function_name</function>(str* res, struct select *s, struct sip_msg *msg)
44
+</emphasis>
45
+	Pointer to the string result workspace <emphasis>res</emphasis>, don't allocate
46
+	memory for the string, but use static buffer. There is no way to free the
47
+	allocated memory.
48
+	Pointer to the parsed select structure <emphasis>s</emphasis>. Holds all names,
49
+	numbers divided by the dot and square bracket notation. Use that if any of the
50
+	part used CONSUME_NEXT_STR or CONSUME_NEXT_INT to get the value.
51
+	Pointer to the processed message structure <emphasis>msg</emphasis>.
52
+</para>
53
+<para>
54
+	<emphasis>FIXUP CALL:</emphasis>
55
+	If you set FIXUP_CALL flag for the final
56
+	function, the fixup call will be done immediatelly after function resolution.
57
+	Such call is indicated with res==NULL &amp;&amp; msg==NULL. Such call can convert any of
58
+	the select's parameter into internal data (can hold one pointer), if you do
59
+	that, set param_type to SEL_PARAM_DATA.
60
+</para>
61
+<para>
62
+	Result code of the function declares if the call was sucessful and if the result is
63
+	valid string or empty string.
64
+</para>
65
+<itemizedlist>
66
+	<listitem><para><emphasis>-1</emphasis> error</para></listitem>
67
+	<listitem><para><emphasis>0</emphasis> success, res contains non-empty string</para></listitem>
68
+	<listitem><para><emphasis>1</emphasis> success, result of select call is empty string (res can be left
69
+	unchanged)</para></listitem>
70
+</itemizedlist>
71
+</section>
72
+<section>
73
+<title>Create module's select parsing table</title>
74
+<para>Define static table of select_row_t type and initialize it directly.
75
+</para>
76
+<para>The table is representation of tree (or oriented graph if you want), where
77
+first column represents current (up-to now) resolved function (starting with NULL),
78
+next two columns define if next parameter is integer or particullar string, next
79
+column is new resolved function which will be tested in next round and the last
80
+column is set of flags.
81
+</para>
82
+<programlisting><![CDATA[
83
+static select_row_t test_sel[] = {
84
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("test"), select_test, 0},
85
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("value"), select_test_val, 0},
86
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("echo"), select_test_echo, CONSUME_NEXT_STR},
87
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
88
+};
89
+]]>
90
+</programlisting>
91
+<para>
92
+So in the previous example, the first line will accept syntax
93
+<emphasis>@test</emphasis> and set the resolved function to select_test. In the
94
+next round, all rows with the select_test in the first column will be used to
95
+match, so the next two lines
96
+are candidates to match depending on the next part of select syntax. If it
97
+matches <emphasis>@test.value</emphasis>, the function is resolved to
98
+select_test_val, if it matches <emphasis>@test.echo.anystring</emphasis>, it is
99
+resolved into select_test_echo. Flag <emphasis>CONSUME_NEXT_STR</emphasis> will
100
+accept any string at the 3rd position. As the <emphasis>OPTIONAL</emphasis> flag
101
+is not present, it won't accept just the <emphasis>@test.echo</emphasis> syntax.
102
+</para>
103
+<para>
104
+The table ends with the <emphasis>NULL</emphasis> in the 1st and 4th column,
105
+other columns are not checked (the notation in the example suits well).
106
+</para>
107
+<para>
108
+At the resolving time all function names must be already defined. For functions which are
109
+not leaves, you can use macro <emphasis>ABSTRACT_F(name)</emphasis> to define empty
110
+function, for working function you can use advance definition using
111
+<emphasis>SELECT_F(name)</emphasis> macro.
112
+</para>
113
+</section>
114
+<!--
115
+TODO:
116
+<section>
117
+<title>Available flag options and their meaning</title>
118
+<para>
119
+<emphasis></emphasis>
120
+</para>
121
+</section>
122
+-->
123
+<section>
124
+<title>Register the created table</title>
125
+<para>
126
+	In the module initialization function call <emphasis>register_select_table(table)</emphasis>
127
+	where table is the parsing tree/table you have defined in previous step.
128
+	This call ensures, that the table will become part of the parsing logic for
129
+	all select framework calls defined in the script file or called by another
130
+	module's parse_select.
131
+</para>
132
+<para>
133
+	<emphasis>NOTE:</emphasis> The tables are inserted into the beginning of the
134
+	list, so the core's table (and thus parseable names and definitions) can be
135
+	overrided by module's function, if it is defined with the same name. To
136
+	avoid such situation, the best practice is to start module's select with the
137
+	module's name. E.g in our example code both select functions start
138
+	with @test...
139
+</para>
140
+</section>
141
+<section>
142
+<title>Example - module defining select extension</title>
143
+<para>Example module <emphasis>test</emphasis>, which defines two select function.
144
+<itemizedlist>
145
+	<listitem><para>
146
+	<emphasis>@test.value</emphasis> - returns value passed as modules parameter
147
+	"value"
148
+	</para></listitem>
149
+	<listitem><para>
150
+	<emphasis>@test.echo.xxx</emphasis> - returns xxx regardless of what you put
151
+	as xxx (shows CONSUME_NEXT_STR option)
152
+	</para></listitem>
153
+</itemizedlist>
154
+</para>
155
+<example id="test.c">
156
+<title>test.c</title>
157
+<programlisting><![CDATA[
158
+#include <string.h>
159
+#include "../../sr_module.h"
160
+#include "../../str.h"
161
+#include "../../dprint.h"
162
+#include "../../select.h"
163
+
164
+MODULE_VERSION
165
+
166
+static cmd_export_t cmds[] = {
167
+	{0, 0, 0, 0, 0}
168
+};
169
+
170
+static char* value=NULL;
171
+
172
+static param_export_t params[] = {
173
+	{"value", STR_PARAM, &value},
174
+	{0, 0, 0}
175
+};
176
+
177
+int select_test_val(str* res, struct select* s, struct sip_msg* msg) {
178
+	DBG("SELECT_TEST_VAL called test_val=%s\n", value);
179
+	res->s=value;
180
+	res->len=strlen(value);
181
+	return 0;
182
+}
183
+
184
+int select_test_echo(str* res, struct select* s, struct sip_msg* msg) {
185
+	DBG("SELECT_TEST_ECHO called\n");
186
+	if (s->params[s->n-1].type==SEL_PARAM_STR) {
187
+		*res=s->params[s->n-1].v.s;
188
+		return 0;
189
+	} else
190
+		return -1;
191
+}
192
+
193
+ABSTRACT_F(select_test)
194
+	
195
+static select_row_t test_sel[] = {
196
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("test"), select_test, 0},
197
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("value"), select_test_val, 0},
198
+	{ select_test, SEL_PARAM_STR, STR_STATIC_INIT("echo"), select_test_echo, CONSUME_NEXT_STR},
199
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
200
+};
201
+ 
202
+static int init(void) {
203
+	register_select_table(test_sel);
204
+	return 0;
205
+};
206
+
207
+struct module_exports exports = {
208
+	"test", 
209
+	cmds,           /* Exported commands */
210
+	0,              /* RPC methods */
211
+	params,         /* Exported parameters */
212
+	init,           /* module initialization function */
213
+	0,              /* response function*/
214
+	0,              /* destroy function */
215
+	0,              /* oncancel function */
216
+	0               /* per-child init function */
217
+};
218
+
219
+]]>
220
+</programlisting>
221
+</example>
222
+</section>
223
+</section>
... ...
@@ -61,6 +61,8 @@
61 61
     <xi:include href="db_interface.xml"/>
62 62
     <xi:include href="locking.xml"/>
63 63
 
64
+	<xi:include href="select_module.xml"/>
65
+
64 66
     <!--    
65 67
     TODO:
66 68
     * How to traverse all headers of given type
... ...
@@ -31,14 +31,22 @@
31 31
  *              DIVERSION flag checked
32 32
  *  2006-02-26  don't free str when changing type STR -> DIVERSION (mma)
33 33
  *				it can't be freeable sometimes (e.g. xlog's select)
34
+ *	2006-05-30  parse_select (mma)
35
+ *	2006-06-02  shm_parse_select (mma)
34 36
  *
35 37
  */
36 38
 
39
+#include <stdio.h>
40
+#include <stdlib.h>
41
+#include <string.h>
42
+#include <unistd.h>
43
+#include <ctype.h>
37 44
 
38 45
 #include "select.h"
39 46
 #include "dprint.h"
40 47
 #include "select_core.h"
41 48
 #include "mem/mem.h"
49
+#include "mem/shm_mem.h"
42 50
 
43 51
 /*
44 52
  * The main parser table list placeholder
... ...
@@ -47,6 +55,101 @@
47 55
  */
48 56
 static select_table_t *select_list = &select_core_table;
49 57
 
58
+/*
59
+ * Parse select string into select structure s
60
+ * moves pointer p to the first unused char
61
+ *
62
+ * Returns -1 error
63
+ *			  p points to the first unconsumed char
64
+ *          0 success
65
+ *			  p points to the first unconsumed char
66
+ *			  s points to the select structure
67
+ */
68
+
69
+int w_parse_select(char**p, select_t* sel)
70
+{
71
+	str name;
72
+	
73
+	if (**p=='@') (*p)++;
74
+	sel->n=0;
75
+	while (isalpha(*(*p))) {
76
+		if (sel->n > MAX_SELECT_PARAMS -2) {
77
+			ERR("parse_select: select depth exceeds max\n");
78
+			goto error;
79
+		}
80
+		name.s=(*p);
81
+		while (isalpha(*(*p)) || isdigit(*(*p)) || (*(*p)=='_')) (*p)++;
82
+		name.len=(*p)-name.s;
83
+		sel->params[sel->n].type=SEL_PARAM_STR;
84
+		sel->params[sel->n].v.s=name;
85
+		DBG("parse_select: part %d: %.*s\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
86
+		sel->n++;
87
+		if (*(*p)=='[') {
88
+			(*p)++; 
89
+			name.s=(*p);
90
+			if (*(*p)=='-') (*p)++;
91
+			while (isdigit(*(*p))) (*p)++;
92
+			name.len=(*p)-name.s;
93
+			if (*(*p)!=']') {
94
+				ERR("parse_select: invalid index, no closing ]\n");
95
+				goto error;
96
+			};
97
+			(*p)++;
98
+			sel->params[sel->n].type=SEL_PARAM_INT;
99
+			sel->params[sel->n].v.i=atoi(name.s);
100
+			DBG("parse_select: part %d: [%d]\n", sel->n, sel->params[sel->n].v.i);
101
+			sel->n++;
102
+		}
103
+		if (*(*p)!='.') break;
104
+		(*p)++;
105
+	};
106
+	if (sel->n==0) {
107
+		ERR("parse_select: invalid select\n");
108
+		goto error;
109
+	};
110
+	DBG("parse_select: end, total elements: %d, calling resolve_select\n", sel->n);
111
+	if (resolve_select(sel)<0) {
112
+		ERR("parse_select: error while resolve_select\n");
113
+		goto error;
114
+	}
115
+	return 0;
116
+	
117
+error:
118
+	return -1;
119
+}
120
+
121
+int parse_select (char** p, select_t** s)
122
+{
123
+	select_t* sel;
124
+	
125
+	sel=(select_t*)pkg_malloc(sizeof(select_t));
126
+	if (!sel) {
127
+		ERR("parse_select: no free memory\n");
128
+		return -1;
129
+	}
130
+	if (w_parse_select(p, sel)<0) {
131
+		return -2;
132
+	}
133
+	*s=sel;
134
+	return 0;
135
+}
136
+
137
+int shm_parse_select (char** p, select_t** s)
138
+{
139
+	select_t* sel;
140
+	
141
+	sel=(select_t*)shm_malloc(sizeof(select_t));
142
+	if (!sel) {
143
+		ERR("parse_select: no free shared memory\n");
144
+		return -1;
145
+	}
146
+	if (w_parse_select(p, sel)<0) {
147
+		return -2;
148
+	}
149
+	*s=sel;
150
+	return 0;
151
+}
152
+
50 153
 int resolve_select(select_t* s)
51 154
 {
52 155
 	select_f f;
... ...
@@ -157,6 +157,29 @@ void print_select(select_t* s);
157 157
  */
158 158
 int register_select_table(select_row_t *table);
159 159
 
160
+/*
161
+ * Tries to parse string pointed by *p (up to first non alpha char) into select structure
162
+ * if parsing succeeded, call resolve_select
163
+ * if resolving passes, returns final structure
164
+ * *p moves to first unused char
165
+ * return 0
166
+ *
167
+ * if memory allocation fails, returns -1
168
+ * if parsing or resolving fails, returns -2
169
+ */
170
+int parse_select (char** p, select_t** s);
171
+
172
+/*
173
+ * Select parser, result is stored in SHARED memory
174
+ * 
175
+ * If you call this, you must ensure, that the string which
176
+ * is beeing parsed MUST be at the same place for all child
177
+ * processes, e.g. allocated in the shared memory as well
178
+ *
179
+ * parameters and results same as parse_select
180
+ */
181
+int shm_parse_select (char** p, select_t** s);
182
+
160 183
 #define SELECT_F(function) extern int function (str* res, select_t* s, struct sip_msg* msg);
161 184
 #define ABSTRACT_F(function) int function (str* res, select_t* s, struct sip_msg* msg) {return -1;}
162 185