BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file imperative.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Imperative statement.
32 *
33 * Synopsis:
34 * imperative(string init_template, list init_args, string deinit_template, list deinit_args, string deinit_timeout)
35 *
36 * Description:
37 * Does the following, in order:
38 * 1. Starts a template process from (init_template, init_args) and waits for it to
39 * initialize completely.
40 * 2. Initiates termination of the process and wait for it to terminate.
41 * 3. Puts the statement UP, then waits for a statement termination request (which may
42 * already have been received).
43 * 4. Starts a template process from (deinit_template, deinit_args) and waits for it
44 * to initialize completely, or for the timeout to elapse.
45 * 5. Initiates termination of the process and wait for it to terminate.
46 * 6. Terminates the statement.
47 *
48 * If init_template="<none>", steps (1-2) are skipped.
49 * If deinit_template="<none>", steps (4-5) are skipped.
50 */
51  
52 #include <stdlib.h>
53 #include <string.h>
54  
55 #include <misc/string_begins_with.h>
56 #include <misc/offset.h>
57  
58 #include <ncd/module_common.h>
59  
60 #include <generated/blog_channel_ncd_imperative.h>
61  
62 #define STATE_INIT_WORKING 1
63 #define STATE_INIT_CLEANING 2
64 #define STATE_UP 3
65 #define STATE_DEINIT_WORKING 4
66 #define STATE_DEINIT_CLEANING 5
67  
68 struct instance {
69 NCDModuleInst *i;
70 NCDValRef deinit_template;
71 NCDValRef deinit_args;
72 BTimer deinit_timer;
73 NCDModuleProcess process;
74 int state;
75 int dying;
76 };
77  
78 static int start_process (struct instance *o, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler);
79 static void go_deinit (struct instance *o);
80 static void init_process_handler_event (NCDModuleProcess *process, int event);
81 static void deinit_process_handler_event (NCDModuleProcess *process, int event);
82 static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
83 static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
84 static void deinit_timer_handler (struct instance *o);
85 static void instance_free (struct instance *o);
86  
87 static int start_process (struct instance *o, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler)
88 {
89 ASSERT(NCDVal_IsString(template_name))
90 ASSERT(NCDVal_IsList(args))
91  
92 // create process
93 if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name, args, handler)) {
94 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
95 return 0;
96 }
97  
98 // set special functions
99 NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj);
100 return 1;
101 }
102  
103 static void go_deinit (struct instance *o)
104 {
105 ASSERT(o->dying)
106  
107 // deinit is no-op?
108 if (ncd_is_none(o->deinit_template)) {
109 instance_free(o);
110 return;
111 }
112  
113 // start deinit process
114 if (!start_process(o, o->deinit_template, o->deinit_args, deinit_process_handler_event)) {
115 instance_free(o);
116 return;
117 }
118  
119 // start timer
120 BReactor_SetTimer(o->i->params->iparams->reactor, &o->deinit_timer);
121  
122 // set state deinit working
123 o->state = STATE_DEINIT_WORKING;
124 }
125  
126 static void init_process_handler_event (NCDModuleProcess *process, int event)
127 {
128 struct instance *o = UPPER_OBJECT(process, struct instance, process);
129  
130 switch (event) {
131 case NCDMODULEPROCESS_EVENT_UP: {
132 ASSERT(o->state == STATE_INIT_WORKING)
133  
134 // start terminating
135 NCDModuleProcess_Terminate(&o->process);
136  
137 // set state init cleaning
138 o->state = STATE_INIT_CLEANING;
139 } break;
140  
141 case NCDMODULEPROCESS_EVENT_TERMINATED: {
142 ASSERT(o->state == STATE_INIT_CLEANING)
143  
144 // free process
145 NCDModuleProcess_Free(&o->process);
146  
147 // were we requested to die aleady?
148 if (o->dying) {
149 go_deinit(o);
150 return;
151 }
152  
153 // signal up
154 NCDModuleInst_Backend_Up(o->i);
155  
156 // set state up
157 o->state = STATE_UP;
158 } break;
159  
160 default: ASSERT(0);
161 }
162 }
163  
164 static void deinit_process_handler_event (NCDModuleProcess *process, int event)
165 {
166 struct instance *o = UPPER_OBJECT(process, struct instance, process);
167 ASSERT(o->dying)
168  
169 switch (event) {
170 case NCDMODULEPROCESS_EVENT_UP: {
171 ASSERT(o->state == STATE_DEINIT_WORKING)
172  
173 // stop timer
174 BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->deinit_timer);
175  
176 // start terminating
177 NCDModuleProcess_Terminate(&o->process);
178  
179 // set state deinit cleaning
180 o->state = STATE_DEINIT_CLEANING;
181 } break;
182  
183 case NCDMODULEPROCESS_EVENT_TERMINATED: {
184 ASSERT(o->state == STATE_DEINIT_CLEANING)
185  
186 // free process
187 NCDModuleProcess_Free(&o->process);
188  
189 // die
190 instance_free(o);
191 return;
192 } break;
193  
194 default: ASSERT(0);
195 }
196 }
197  
198 static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
199 {
200 struct instance *o = UPPER_OBJECT(process, struct instance, process);
201 ASSERT(o->state != STATE_UP)
202  
203 if (name == NCD_STRING_CALLER) {
204 *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj);
205 return 1;
206 }
207  
208 return 0;
209 }
210  
211 static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
212 {
213 struct instance *o = NCDObject_DataPtr(obj);
214 ASSERT(o->state != STATE_UP)
215  
216 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
217 }
218  
219 static void deinit_timer_handler (struct instance *o)
220 {
221 ASSERT(o->state == STATE_DEINIT_WORKING)
222  
223 ModuleLog(o->i, BLOG_ERROR, "imperative deinit timeout elapsed");
224  
225 // start terminating
226 NCDModuleProcess_Terminate(&o->process);
227  
228 // set state deinit cleaning
229 o->state = STATE_DEINIT_CLEANING;
230 }
231  
232 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
233 {
234 struct instance *o = vo;
235 o->i = i;
236  
237 // check arguments
238 NCDValRef init_template_arg;
239 NCDValRef init_args;
240 NCDValRef deinit_template_arg;
241 NCDValRef deinit_timeout_arg;
242 if (!NCDVal_ListRead(params->args, 5, &init_template_arg, &init_args, &deinit_template_arg, &o->deinit_args, &deinit_timeout_arg)) {
243 ModuleLog(i, BLOG_ERROR, "wrong arity");
244 goto fail0;
245 }
246 if (!NCDVal_IsString(init_template_arg) || !NCDVal_IsList(init_args) ||
247 !NCDVal_IsString(deinit_template_arg) || !NCDVal_IsList(o->deinit_args)) {
248 ModuleLog(i, BLOG_ERROR, "wrong type");
249 goto fail0;
250 }
251  
252 o->deinit_template = deinit_template_arg;
253  
254 // read timeout
255 uintmax_t timeout;
256 if (!ncd_read_uintmax(deinit_timeout_arg, &timeout) || timeout > UINT64_MAX){
257 ModuleLog(i, BLOG_ERROR, "wrong timeout");
258 goto fail0;
259 }
260  
261 // init timer
262 BTimer_Init(&o->deinit_timer, timeout, (BTimer_handler)deinit_timer_handler, o);
263  
264 if (ncd_is_none(init_template_arg)) {
265 // signal up
266 NCDModuleInst_Backend_Up(i);
267  
268 // set state up
269 o->state = STATE_UP;
270 } else {
271 // start init process
272 if (!start_process(o, init_template_arg, init_args, init_process_handler_event)) {
273 goto fail0;
274 }
275  
276 // set state init working
277 o->state = STATE_INIT_WORKING;
278 }
279  
280 // set not dying
281 o->dying = 0;
282 return;
283  
284 fail0:
285 NCDModuleInst_Backend_DeadError(i);
286 }
287  
288 static void instance_free (struct instance *o)
289 {
290 NCDModuleInst_Backend_Dead(o->i);
291 }
292  
293 static void func_die (void *vo)
294 {
295 struct instance *o = vo;
296 ASSERT(!o->dying)
297  
298 // set dying
299 o->dying = 1;
300  
301 if (o->state == STATE_UP) {
302 go_deinit(o);
303 return;
304 }
305 }
306  
307 static struct NCDModule modules[] = {
308 {
309 .type = "imperative",
310 .func_new2 = func_new,
311 .func_die = func_die,
312 .alloc_size = sizeof(struct instance)
313 }, {
314 .type = NULL
315 }
316 };
317  
318 const struct NCDModuleGroup ncdmodule_imperative = {
319 .modules = modules
320 };