BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file process_manager.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 allows starting and controlling template processes using an imperative
32 * interface.
33 *
34 * Synopsis:
35 * process_manager()
36 *
37 * Description:
38 * Represents a set of managed processes. Each process has a "name", which is a value
39 * that uniquely identifies it within its process manager.
40 * When deinitialization is requested, requests termination of all managed processes
41 * and waits for all of them to terminate before deinitializing.
42 * Managed processes can access objects as seen from the process_manager() statement
43 * via the special _caller object.
44 *
45 * Synopsis:
46 * process_manager::start(name, string template_name, list args)
47 * process_manager::start(string template_name, list args)
48 *
49 * Description:
50 * Creates a new process from the template named 'template_name', with arguments 'args',
51 * identified by 'name' within the process manager. If the two-argument form of start() is
52 * used, the process does not have a name, and cannot be imperatively stopped using
53 * stop().
54 * If a process with this name already exists and is not being terminated, does nothing.
55 * If it exists and is being terminated, it will be restarted using the given parameters
56 * after it terminates. If the process does not exist, it is created immediately, and the
57 * immediate effects of the process being created happnen before the immediate effects of
58 * the start() statement going up.
59 *
60 * Synopsis:
61 * process_manager::stop(name)
62 *
63 * Description:
64 * Initiates termination of the process identified by 'name' within the process manager.
65 * If there is no such process, or the process is already being terminated, does nothing.
66 * If the process does exist and is not already being terminated, termination of the
67 * process is requested, and the immediate effects of the termination request happen
68 * before the immediate effects of the stop() statement going up.
69 */
70  
71 #include <stdlib.h>
72 #include <string.h>
73  
74 #include <misc/offset.h>
75 #include <misc/debug.h>
76 #include <misc/strdup.h>
77 #include <misc/balloc.h>
78 #include <structure/LinkedList1.h>
79  
80 #include <ncd/module_common.h>
81  
82 #include <generated/blog_channel_ncd_process_manager.h>
83  
84 #define RETRY_TIME 10000
85  
86 #define PROCESS_STATE_RUNNING 1
87 #define PROCESS_STATE_STOPPING 2
88 #define PROCESS_STATE_RESTARTING 3
89 #define PROCESS_STATE_RETRYING 4
90  
91 struct instance {
92 NCDModuleInst *i;
93 LinkedList1 processes_list;
94 int dying;
95 };
96  
97 struct process {
98 struct instance *manager;
99 LinkedList1Node processes_list_node;
100 BSmallTimer retry_timer; // running if state=retrying
101 int state;
102 NCD_string_id_t template_name;
103 NCDValMem current_mem;
104 NCDValSafeRef current_name;
105 NCDValSafeRef current_args;
106 NCDValMem next_mem; // next_* if state=restarting
107 NCDValSafeRef next_name;
108 NCDValSafeRef next_args;
109 NCDModuleProcess module_process; // if state!=retrying
110 };
111  
112 static struct process * find_process (struct instance *o, NCDValRef name);
113 static int process_new (struct instance *o, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args);
114 static void process_free (struct process *p);
115 static void process_try (struct process *p);
116 static void process_retry_timer_handler (BSmallTimer *retry_timer);
117 static void process_module_process_handler_event (NCDModuleProcess *module_process, int event);
118 static int process_module_process_func_getspecialobj (NCDModuleProcess *module_process, NCD_string_id_t name, NCDObject *out_object);
119 static int process_module_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
120 static void process_stop (struct process *p);
121 static int process_restart (struct process *p, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args);
122 static void instance_free (struct instance *o);
123  
124 static struct process * find_process (struct instance *o, NCDValRef name)
125 {
126 ASSERT(!NCDVal_IsInvalid(name))
127  
128 LinkedList1Node *n = LinkedList1_GetFirst(&o->processes_list);
129 while (n) {
130 struct process *p = UPPER_OBJECT(n, struct process, processes_list_node);
131 ASSERT(p->manager == o)
132 if (!NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) && NCDVal_Compare(NCDVal_FromSafe(&p->current_mem, p->current_name), name) == 0) {
133 return p;
134 }
135 n = LinkedList1Node_Next(n);
136 }
137  
138 return NULL;
139 }
140  
141 static int process_new (struct instance *o, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args)
142 {
143 ASSERT(!o->dying)
144 ASSERT(NCDVal_IsInvalid(NCDVal_FromSafe(mem, name)) || !find_process(o, NCDVal_FromSafe(mem, name)))
145 ASSERT(NCDVal_IsString(NCDVal_FromSafe(mem, template_name)))
146 ASSERT(NCDVal_IsList(NCDVal_FromSafe(mem, args)))
147  
148 // allocate structure
149 struct process *p = BAlloc(sizeof(*p));
150 if (!p) {
151 ModuleLog(o->i, BLOG_ERROR, "BAlloc failed");
152 goto fail0;
153 }
154  
155 // set manager
156 p->manager = o;
157  
158 // insert to processes list
159 LinkedList1_Append(&o->processes_list, &p->processes_list_node);
160  
161 // init retry timer
162 BSmallTimer_Init(&p->retry_timer, process_retry_timer_handler);
163  
164 // init template name
165 p->template_name = ncd_get_string_id(NCDVal_FromSafe(mem, template_name));
166 if (p->template_name < 0) {
167 ModuleLog(o->i, BLOG_ERROR, "ncd_get_string_id failed");
168 goto fail1;
169 }
170  
171 // init current mem as a copy of mem
172 if (!NCDValMem_InitCopy(&p->current_mem, mem)) {
173 ModuleLog(o->i, BLOG_ERROR, "NCDValMem_InitCopy failed");
174 goto fail1;
175 }
176  
177 // remember name and args
178 p->current_name = name;
179 p->current_args = args;
180  
181 // try starting it
182 process_try(p);
183 return 1;
184  
185 fail1:
186 LinkedList1_Remove(&o->processes_list, &p->processes_list_node);
187 BFree(p);
188 fail0:
189 return 0;
190 }
191  
192 static void process_free (struct process *p)
193 {
194 struct instance *o = p->manager;
195  
196 // free current mem
197 NCDValMem_Free(&p->current_mem);
198  
199 // free timer
200 BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &p->retry_timer);
201  
202 // remove from processes list
203 LinkedList1_Remove(&o->processes_list, &p->processes_list_node);
204  
205 // free structure
206 BFree(p);
207 }
208  
209 static void process_try (struct process *p)
210 {
211 struct instance *o = p->manager;
212 ASSERT(!o->dying)
213 ASSERT(!BSmallTimer_IsRunning(&p->retry_timer))
214  
215 // init module process
216 if (!NCDModuleProcess_InitId(&p->module_process, o->i, p->template_name, NCDVal_FromSafe(&p->current_mem, p->current_args), process_module_process_handler_event)) {
217 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
218 goto fail;
219 }
220  
221 // set special objects function
222 NCDModuleProcess_SetSpecialFuncs(&p->module_process, process_module_process_func_getspecialobj);
223  
224 // set state
225 p->state = PROCESS_STATE_RUNNING;
226 return;
227  
228 fail:
229 // set timer
230 BReactor_SetSmallTimer(o->i->params->iparams->reactor, &p->retry_timer, BTIMER_SET_RELATIVE, RETRY_TIME);
231  
232 // set state
233 p->state = PROCESS_STATE_RETRYING;
234 }
235  
236 static void process_retry_timer_handler (BSmallTimer *retry_timer)
237 {
238 struct process *p = UPPER_OBJECT(retry_timer, struct process, retry_timer);
239 struct instance *o = p->manager;
240 B_USE(o)
241 ASSERT(p->state == PROCESS_STATE_RETRYING)
242 ASSERT(!o->dying)
243  
244 // retry
245 process_try(p);
246 }
247  
248 void process_module_process_handler_event (NCDModuleProcess *module_process, int event)
249 {
250 struct process *p = UPPER_OBJECT(module_process, struct process, module_process);
251 struct instance *o = p->manager;
252 ASSERT(p->state != PROCESS_STATE_RETRYING)
253 ASSERT(p->state != PROCESS_STATE_RESTARTING || !o->dying)
254 ASSERT(!BSmallTimer_IsRunning(&p->retry_timer))
255  
256 switch (event) {
257 case NCDMODULEPROCESS_EVENT_UP: {
258 ASSERT(p->state == PROCESS_STATE_RUNNING)
259 } break;
260  
261 case NCDMODULEPROCESS_EVENT_DOWN: {
262 ASSERT(p->state == PROCESS_STATE_RUNNING)
263  
264 // allow process to continue
265 NCDModuleProcess_Continue(&p->module_process);
266 } break;
267  
268 case NCDMODULEPROCESS_EVENT_TERMINATED: {
269 ASSERT(p->state == PROCESS_STATE_RESTARTING || p->state == PROCESS_STATE_STOPPING)
270  
271 // free module process
272 NCDModuleProcess_Free(&p->module_process);
273  
274 if (p->state == PROCESS_STATE_RESTARTING) {
275 // free current mem
276 NCDValMem_Free(&p->current_mem);
277  
278 // move next mem/values over current mem/values
279 p->current_mem = p->next_mem;
280 p->current_name = p->next_name;
281 p->current_args = p->next_args;
282  
283 // try starting it again
284 process_try(p);
285 return;
286 }
287  
288 // free process
289 process_free(p);
290  
291 // if manager is dying and there are no more processes, let it die
292 if (o->dying && LinkedList1_IsEmpty(&o->processes_list)) {
293 instance_free(o);
294 }
295 } break;
296 }
297 }
298  
299 static int process_module_process_func_getspecialobj (NCDModuleProcess *module_process, NCD_string_id_t name, NCDObject *out_object)
300 {
301 struct process *p = UPPER_OBJECT(module_process, struct process, module_process);
302 ASSERT(p->state != PROCESS_STATE_RETRYING)
303  
304 if (name == NCD_STRING_CALLER) {
305 *out_object = NCDObject_Build(-1, p, NCDObject_no_getvar, process_module_process_caller_obj_func_getobj);
306 return 1;
307 }
308  
309 return 0;
310 }
311  
312 static int process_module_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
313 {
314 struct process *p = NCDObject_DataPtr(obj);
315 struct instance *o = p->manager;
316 ASSERT(p->state != PROCESS_STATE_RETRYING)
317  
318 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
319 }
320  
321 static void process_stop (struct process *p)
322 {
323 switch (p->state) {
324 case PROCESS_STATE_RETRYING: {
325 // free process
326 process_free(p);
327 } break;
328  
329 case PROCESS_STATE_RUNNING: {
330 // request process to terminate
331 NCDModuleProcess_Terminate(&p->module_process);
332  
333 // set state
334 p->state = PROCESS_STATE_STOPPING;
335 } break;
336  
337 case PROCESS_STATE_RESTARTING: {
338 // free next mem
339 NCDValMem_Free(&p->next_mem);
340  
341 // set state
342 p->state = PROCESS_STATE_STOPPING;
343 } break;
344  
345 case PROCESS_STATE_STOPPING: {
346 // nothing to do
347 } break;
348  
349 default: ASSERT(0);
350 }
351 }
352  
353 static int process_restart (struct process *p, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args)
354 {
355 struct instance *o = p->manager;
356 ASSERT(!o->dying)
357 ASSERT(p->state == PROCESS_STATE_STOPPING)
358 ASSERT(!NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) || NCDVal_IsInvalid(NCDVal_FromSafe(mem, name)))
359 ASSERT(NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) || NCDVal_Compare(NCDVal_FromSafe(mem, name), NCDVal_FromSafe(&p->current_mem, p->current_name)) == 0)
360 ASSERT(NCDVal_IsString(NCDVal_FromSafe(mem, template_name)))
361 ASSERT(NCDVal_IsList(NCDVal_FromSafe(mem, args)))
362  
363 // copy mem to next mem
364 if (!NCDValMem_InitCopy(&p->next_mem, mem)) {
365 ModuleLog(o->i, BLOG_ERROR, "NCDValMem_InitCopy failed");
366 goto fail0;
367 }
368  
369 // remember name and args to next
370 p->next_name = name;
371 p->next_args = args;
372  
373 // set state
374 p->state = PROCESS_STATE_RESTARTING;
375 return 1;
376  
377 fail0:
378 return 0;
379 }
380  
381 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
382 {
383 struct instance *o = vo;
384 o->i = i;
385  
386 // check arguments
387 if (!NCDVal_ListRead(params->args, 0)) {
388 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
389 goto fail0;
390 }
391  
392 // init processes list
393 LinkedList1_Init(&o->processes_list);
394  
395 // set not dying
396 o->dying = 0;
397  
398 // signal up
399 NCDModuleInst_Backend_Up(o->i);
400 return;
401  
402 fail0:
403 NCDModuleInst_Backend_DeadError(i);
404 }
405  
406 void instance_free (struct instance *o)
407 {
408 ASSERT(LinkedList1_IsEmpty(&o->processes_list))
409  
410 NCDModuleInst_Backend_Dead(o->i);
411 }
412  
413 static void func_die (void *vo)
414 {
415 struct instance *o = vo;
416 ASSERT(!o->dying)
417  
418 // request all processes to die
419 LinkedList1Node *n = LinkedList1_GetFirst(&o->processes_list);
420 while (n) {
421 LinkedList1Node *next = LinkedList1Node_Next(n);
422 struct process *p = UPPER_OBJECT(n, struct process, processes_list_node);
423 process_stop(p);
424 n = next;
425 }
426  
427 // if there are no processes, die immediately
428 if (LinkedList1_IsEmpty(&o->processes_list)) {
429 instance_free(o);
430 return;
431 }
432  
433 // set dying
434 o->dying = 1;
435 }
436  
437 static void start_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
438 {
439 // check arguments
440 NCDValRef name_arg = NCDVal_NewInvalid();
441 NCDValRef template_name_arg;
442 NCDValRef args_arg;
443 if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg) &&
444 !NCDVal_ListRead(params->args, 3, &name_arg, &template_name_arg, &args_arg)
445 ) {
446 ModuleLog(i, BLOG_ERROR, "wrong arity");
447 goto fail0;
448 }
449 if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) {
450 ModuleLog(i, BLOG_ERROR, "wrong type");
451 goto fail0;
452 }
453  
454 // signal up.
455 // Do it before creating the process so that the process starts initializing before our own process continues.
456 NCDModuleInst_Backend_Up(i);
457  
458 // get method object
459 struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
460  
461 if (!mo->dying) {
462 struct process *p = (NCDVal_IsInvalid(name_arg) ? NULL : find_process(mo, name_arg));
463 if (!p || p->state == PROCESS_STATE_STOPPING) {
464 if (p) {
465 if (!process_restart(p, args_arg.mem, NCDVal_ToSafe(name_arg), NCDVal_ToSafe(template_name_arg), NCDVal_ToSafe(args_arg))) {
466 ModuleLog(i, BLOG_ERROR, "failed to restart process");
467 goto fail0;
468 }
469 } else {
470 if (!process_new(mo, args_arg.mem, NCDVal_ToSafe(name_arg), NCDVal_ToSafe(template_name_arg), NCDVal_ToSafe(args_arg))) {
471 ModuleLog(i, BLOG_ERROR, "failed to create process");
472 goto fail0;
473 }
474 }
475 }
476 }
477  
478 return;
479  
480 fail0:
481 NCDModuleInst_Backend_DeadError(i);
482 }
483  
484 static void stop_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
485 {
486 // check arguments
487 NCDValRef name_arg;
488 if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
489 ModuleLog(i, BLOG_ERROR, "wrong arity");
490 goto fail0;
491 }
492  
493 // signal up.
494 // Do it before stopping the process so that the process starts terminating before our own process continues.
495 NCDModuleInst_Backend_Up(i);
496  
497 // get method object
498 struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
499  
500 if (!mo->dying) {
501 struct process *p = find_process(mo, name_arg);
502 if (p && p->state != PROCESS_STATE_STOPPING) {
503 process_stop(p);
504 }
505 }
506  
507 return;
508  
509 fail0:
510 NCDModuleInst_Backend_DeadError(i);
511 }
512  
513 static struct NCDModule modules[] = {
514 {
515 .type = "process_manager",
516 .func_new2 = func_new,
517 .func_die = func_die,
518 .alloc_size = sizeof(struct instance)
519 }, {
520 .type = "process_manager::start",
521 .func_new2 = start_func_new
522 }, {
523 .type = "process_manager::stop",
524 .func_new2 = stop_func_new
525 }, {
526 .type = NULL
527 }
528 };
529  
530 const struct NCDModuleGroup ncdmodule_process_manager = {
531 .modules = modules
532 };