BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file spawn.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 * Module which starts a process from a process template on initialization, and
32 * stops it on deinitialization.
33 *
34 * Synopsis:
35 * spawn(string template_name, list args)
36 *
37 * Description:
38 * On initialization, creates a new process from the template named
39 * 'template_name', with arguments 'args'. On deinitialization, initiates termination
40 * of the process and waits for it to terminate. The process can access objects
41 * as seen from 'spawn' via the _caller special object.
42 *
43 * Synopsis:
44 * spawn::join()
45 *
46 * Description:
47 * A join() on a spawn() is like a depend() on a provide() which is located at the
48 * end of the spawned process.
49 *
50 * Variables:
51 * Exposes objects from the spawned process.
52 */
53  
54 #include <stdlib.h>
55 #include <string.h>
56  
57 #include <misc/offset.h>
58 #include <misc/debug.h>
59 #include <structure/LinkedList0.h>
60  
61 #include <ncd/module_common.h>
62  
63 #include <generated/blog_channel_ncd_spawn.h>
64  
65 #define STATE_WORKING 1
66 #define STATE_UP 2
67 #define STATE_WAITING 3
68 #define STATE_WAITING_TERMINATING 4
69 #define STATE_TERMINATING 5
70  
71 struct instance {
72 NCDModuleInst *i;
73 NCDModuleProcess process;
74 LinkedList0 clean_list;
75 LinkedList0 dirty_list;
76 int state;
77 };
78  
79 struct join_instance {
80 NCDModuleInst *i;
81 struct instance *spawn;
82 LinkedList0Node list_node;
83 int is_dirty;
84 };
85  
86 static void assert_dirty_state (struct instance *o);
87 static void process_handler_event (NCDModuleProcess *process, int event);
88 static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
89 static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
90 static void bring_joins_down (struct instance *o);
91 static void continue_working (struct instance *o);
92 static void continue_terminating (struct instance *o);
93 static void instance_free (struct instance *o);
94  
95 static void assert_dirty_state (struct instance *o)
96 {
97 ASSERT(!LinkedList0_IsEmpty(&o->dirty_list) == (o->state == STATE_WAITING || o->state == STATE_WAITING_TERMINATING))
98 }
99  
100 static void process_handler_event (NCDModuleProcess *process, int event)
101 {
102 struct instance *o = UPPER_OBJECT(process, struct instance, process);
103 assert_dirty_state(o);
104  
105 switch (event) {
106 case NCDMODULEPROCESS_EVENT_UP: {
107 ASSERT(o->state == STATE_WORKING)
108 ASSERT(LinkedList0_IsEmpty(&o->dirty_list))
109  
110 // set state up
111 o->state = STATE_UP;
112  
113 // bring joins up
114 for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->clean_list); ln; ln = LinkedList0Node_Next(ln)) {
115 struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node);
116 ASSERT(join->spawn == o)
117 ASSERT(!join->is_dirty)
118 NCDModuleInst_Backend_Up(join->i);
119 }
120 } break;
121  
122 case NCDMODULEPROCESS_EVENT_DOWN: {
123 ASSERT(o->state == STATE_UP)
124 ASSERT(LinkedList0_IsEmpty(&o->dirty_list))
125  
126 // bring joins down, moving them to the dirty list
127 bring_joins_down(o);
128  
129 // set state waiting
130 o->state = STATE_WAITING;
131  
132 // if we have no joins, continue immediately
133 if (LinkedList0_IsEmpty(&o->dirty_list)) {
134 continue_working(o);
135 return;
136 }
137 } break;
138  
139 case NCDMODULEPROCESS_EVENT_TERMINATED: {
140 ASSERT(o->state == STATE_TERMINATING)
141 ASSERT(LinkedList0_IsEmpty(&o->dirty_list))
142  
143 // die finally
144 instance_free(o);
145 return;
146 } break;
147  
148 default: ASSERT(0);
149 }
150 }
151  
152 static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
153 {
154 struct instance *o = UPPER_OBJECT(process, struct instance, process);
155  
156 if (name == NCD_STRING_CALLER) {
157 *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj);
158 return 1;
159 }
160  
161 return 0;
162 }
163  
164 static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
165 {
166 struct instance *o = NCDObject_DataPtr(obj);
167  
168 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
169 }
170  
171 static void bring_joins_down (struct instance *o)
172 {
173 LinkedList0Node *ln;
174 while (ln = LinkedList0_GetFirst(&o->clean_list)) {
175 struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node);
176 ASSERT(join->spawn == o)
177 ASSERT(!join->is_dirty)
178 NCDModuleInst_Backend_Down(join->i);
179 LinkedList0_Remove(&o->clean_list, &join->list_node);
180 LinkedList0_Prepend(&o->dirty_list, &join->list_node);
181 join->is_dirty = 1;
182 }
183 }
184  
185 static void continue_working (struct instance *o)
186 {
187 ASSERT(o->state == STATE_WAITING)
188 ASSERT(LinkedList0_IsEmpty(&o->dirty_list))
189  
190 // continue process
191 NCDModuleProcess_Continue(&o->process);
192  
193 // set state working
194 o->state = STATE_WORKING;
195 }
196  
197 static void continue_terminating (struct instance *o)
198 {
199 ASSERT(o->state == STATE_WAITING_TERMINATING)
200 ASSERT(LinkedList0_IsEmpty(&o->dirty_list))
201  
202 // request process to terminate
203 NCDModuleProcess_Terminate(&o->process);
204  
205 // set state terminating
206 o->state = STATE_TERMINATING;
207 }
208  
209 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
210 {
211 struct instance *o = vo;
212 o->i = i;
213  
214 // check arguments
215 NCDValRef template_name_arg;
216 NCDValRef args_arg;
217 if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) {
218 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
219 goto fail0;
220 }
221 if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) {
222 ModuleLog(o->i, BLOG_ERROR, "wrong type");
223 goto fail0;
224 }
225  
226 // signal up.
227 // Do it before creating the process so that the process starts initializing before our own process continues.
228 NCDModuleInst_Backend_Up(o->i);
229  
230 // create process
231 if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name_arg, args_arg, process_handler_event)) {
232 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
233 goto fail0;
234 }
235  
236 // set object resolution function
237 NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj);
238  
239 // init lists
240 LinkedList0_Init(&o->clean_list);
241 LinkedList0_Init(&o->dirty_list);
242  
243 // set state working
244 o->state = STATE_WORKING;
245 return;
246  
247 fail0:
248 NCDModuleInst_Backend_DeadError(i);
249 }
250  
251 void instance_free (struct instance *o)
252 {
253 ASSERT(LinkedList0_IsEmpty(&o->dirty_list))
254  
255 // unlink joins
256 LinkedList0Node *ln;
257 while (ln = LinkedList0_GetFirst(&o->clean_list)) {
258 struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node);
259 ASSERT(join->spawn == o)
260 ASSERT(!join->is_dirty)
261 LinkedList0_Remove(&o->clean_list, &join->list_node);
262 join->spawn = NULL;
263 }
264  
265 // free process
266 NCDModuleProcess_Free(&o->process);
267  
268 NCDModuleInst_Backend_Dead(o->i);
269 }
270  
271 static void func_die (void *vo)
272 {
273 struct instance *o = vo;
274 ASSERT(o->state != STATE_WAITING_TERMINATING)
275 ASSERT(o->state != STATE_TERMINATING)
276 assert_dirty_state(o);
277  
278 // bring joins down
279 if (o->state == STATE_UP) {
280 bring_joins_down(o);
281 }
282  
283 // set state waiting terminating
284 o->state = STATE_WAITING_TERMINATING;
285  
286 // start terminating now if possible
287 if (LinkedList0_IsEmpty(&o->dirty_list)) {
288 continue_terminating(o);
289 return;
290 }
291 }
292  
293 static void join_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
294 {
295 struct join_instance *o = vo;
296 o->i = i;
297  
298 if (!NCDVal_ListRead(params->args, 0)) {
299 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
300 goto fail0;
301 }
302  
303 o->spawn = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
304 assert_dirty_state(o->spawn);
305  
306 LinkedList0_Prepend(&o->spawn->clean_list, &o->list_node);
307 o->is_dirty = 0;
308  
309 if (o->spawn->state == STATE_UP) {
310 NCDModuleInst_Backend_Up(i);
311 }
312  
313 return;
314  
315 fail0:
316 NCDModuleInst_Backend_DeadError(i);
317 }
318  
319 static void join_func_die (void *vo)
320 {
321 struct join_instance *o = vo;
322  
323 if (o->spawn) {
324 assert_dirty_state(o->spawn);
325  
326 // remove from list
327 if (o->is_dirty) {
328 LinkedList0_Remove(&o->spawn->dirty_list, &o->list_node);
329 } else {
330 LinkedList0_Remove(&o->spawn->clean_list, &o->list_node);
331 }
332  
333 if (o->is_dirty && LinkedList0_IsEmpty(&o->spawn->dirty_list)) {
334 ASSERT(o->spawn->state == STATE_WAITING || o->spawn->state == STATE_WAITING_TERMINATING)
335  
336 if (o->spawn->state == STATE_WAITING) {
337 continue_working(o->spawn);
338 } else {
339 continue_terminating(o->spawn);
340 }
341 }
342 }
343  
344 NCDModuleInst_Backend_Dead(o->i);
345 }
346  
347 static int join_func_getobj (void *vo, NCD_string_id_t name, NCDObject *out)
348 {
349 struct join_instance *o = vo;
350  
351 if (!o->spawn) {
352 return 0;
353 }
354  
355 return NCDModuleProcess_GetObj(&o->spawn->process, name, out);
356 }
357  
358 static void join_func_clean (void *vo)
359 {
360 struct join_instance *o = vo;
361  
362 if (!(o->spawn && o->is_dirty)) {
363 return;
364 }
365  
366 assert_dirty_state(o->spawn);
367 ASSERT(o->spawn->state == STATE_WAITING || o->spawn->state == STATE_WAITING_TERMINATING)
368  
369 LinkedList0_Remove(&o->spawn->dirty_list, &o->list_node);
370 LinkedList0_Prepend(&o->spawn->clean_list, &o->list_node);
371 o->is_dirty = 0;
372  
373 if (LinkedList0_IsEmpty(&o->spawn->dirty_list)) {
374 if (o->spawn->state == STATE_WAITING) {
375 continue_working(o->spawn);
376 } else {
377 continue_terminating(o->spawn);
378 }
379 }
380 }
381  
382 static struct NCDModule modules[] = {
383 {
384 .type = "spawn",
385 .func_new2 = func_new,
386 .func_die = func_die,
387 .alloc_size = sizeof(struct instance)
388 }, {
389 .type = "synchronous_process", // deprecated name
390 .func_new2 = func_new,
391 .func_die = func_die,
392 .alloc_size = sizeof(struct instance)
393 }, {
394 .type = "spawn::join",
395 .func_new2 = join_func_new,
396 .func_die = join_func_die,
397 .func_getobj = join_func_getobj,
398 .func_clean = join_func_clean,
399 .alloc_size = sizeof(struct join_instance),
400 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN
401 }, {
402 .type = NULL
403 }
404 };
405  
406 const struct NCDModuleGroup ncdmodule_spawn = {
407 .modules = modules
408 };