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