BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file ondemand.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 * On-demand process manager.
32 *
33 * Synopsis:
34 * ondemand(string template_name, list args)
35 *
36 * Description:
37 * Manages an on-demand template process using a process template named
38 * template_name.
39 * On deinitialization, if the process is running, reqests its termination
40 * and waits for it to terminate.
41 *
42 * Synopsis:
43 * ondemand::demand()
44 *
45 * Description:
46 * Demands the availability of an on-demand template process.
47 * This statement is in UP state if and only if the template process of the
48 * corresponding ondemand object is completely up.
49 *
50 * Variables:
51 * Exposes variables and objects from the template process corresponding to
52 * the ondemand object.
53 */
54  
55 #include <stdlib.h>
56 #include <string.h>
57  
58 #include <misc/offset.h>
59 #include <misc/debug.h>
60 #include <structure/LinkedList1.h>
61  
62 #include <ncd/module_common.h>
63  
64 #include <generated/blog_channel_ncd_ondemand.h>
65  
66 struct ondemand {
67 NCDModuleInst *i;
68 NCDValRef template_name;
69 NCDValRef args;
70 LinkedList1 demands_list;
71 int dying;
72 int have_process;
73 NCDModuleProcess process;
74 int process_terminating;
75 int process_up;
76 };
77  
78 struct demand {
79 NCDModuleInst *i;
80 struct ondemand *od;
81 LinkedList1Node demands_list_node;
82 };
83  
84 static int ondemand_start_process (struct ondemand *o);
85 static void ondemand_terminate_process (struct ondemand *o);
86 static void ondemand_process_handler (NCDModuleProcess *process, int event);
87 static void ondemand_free (struct ondemand *o);
88 static void demand_free (struct demand *o, int is_error);
89  
90 static int ondemand_start_process (struct ondemand *o)
91 {
92 ASSERT(!o->dying)
93 ASSERT(!o->have_process)
94  
95 // start process
96 if (!NCDModuleProcess_InitValue(&o->process, o->i, o->template_name, o->args, ondemand_process_handler)) {
97 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
98 goto fail0;
99 }
100  
101 // set have process
102 o->have_process = 1;
103  
104 // set process not terminating
105 o->process_terminating = 0;
106  
107 // set process not up
108 o->process_up = 0;
109  
110 return 1;
111  
112 fail0:
113 return 0;
114 }
115  
116 static void ondemand_terminate_process (struct ondemand *o)
117 {
118 ASSERT(o->have_process)
119 ASSERT(!o->process_terminating)
120  
121 // request termination
122 NCDModuleProcess_Terminate(&o->process);
123  
124 // set process terminating
125 o->process_terminating = 1;
126  
127 if (o->process_up) {
128 // set process down
129 o->process_up = 0;
130  
131 // signal demands down
132 for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) {
133 struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node);
134 ASSERT(demand->od == o)
135 NCDModuleInst_Backend_Down(demand->i);
136 }
137 }
138 }
139  
140 static void ondemand_process_handler (NCDModuleProcess *process, int event)
141 {
142 struct ondemand *o = UPPER_OBJECT(process, struct ondemand, process);
143 ASSERT(o->have_process)
144  
145 switch (event) {
146 case NCDMODULEPROCESS_EVENT_UP: {
147 ASSERT(!o->process_terminating)
148 ASSERT(!o->process_up)
149  
150 // set process up
151 o->process_up = 1;
152  
153 // signal demands up
154 for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) {
155 struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node);
156 ASSERT(demand->od == o)
157 NCDModuleInst_Backend_Up(demand->i);
158 }
159 } break;
160  
161 case NCDMODULEPROCESS_EVENT_DOWN: {
162 ASSERT(!o->process_terminating)
163 ASSERT(o->process_up)
164  
165 // continue process
166 NCDModuleProcess_Continue(&o->process);
167  
168 // set process down
169 o->process_up = 0;
170  
171 // signal demands down
172 for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) {
173 struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node);
174 ASSERT(demand->od == o)
175 NCDModuleInst_Backend_Down(demand->i);
176 }
177 } break;
178  
179 case NCDMODULEPROCESS_EVENT_TERMINATED: {
180 ASSERT(o->process_terminating)
181 ASSERT(!o->process_up)
182  
183 // free process
184 NCDModuleProcess_Free(&o->process);
185  
186 // set have no process
187 o->have_process = 0;
188  
189 // if dying, die finally
190 if (o->dying) {
191 ondemand_free(o);
192 return;
193 }
194  
195 // if demands arrivied, restart process
196 if (!LinkedList1_IsEmpty(&o->demands_list)) {
197 if (!ondemand_start_process(o)) {
198 // error demands
199 while (!LinkedList1_IsEmpty(&o->demands_list)) {
200 struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node);
201 ASSERT(demand->od == o)
202 demand_free(demand, 1);
203 }
204 }
205 }
206 } break;
207 }
208 }
209  
210 static void ondemand_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
211 {
212 struct ondemand *o = vo;
213 o->i = i;
214  
215 // read arguments
216 NCDValRef arg_template_name;
217 NCDValRef arg_args;
218 if (!NCDVal_ListRead(params->args, 2, &arg_template_name, &arg_args)) {
219 ModuleLog(i, BLOG_ERROR, "wrong arity");
220 goto fail0;
221 }
222 if (!NCDVal_IsString(arg_template_name) || !NCDVal_IsList(arg_args)) {
223 ModuleLog(i, BLOG_ERROR, "wrong type");
224 goto fail0;
225 }
226 o->template_name = arg_template_name;
227 o->args = arg_args;
228  
229 // init demands list
230 LinkedList1_Init(&o->demands_list);
231  
232 // set not dying
233 o->dying = 0;
234  
235 // set have no process
236 o->have_process = 0;
237  
238 // signal up
239 NCDModuleInst_Backend_Up(i);
240 return;
241  
242 fail0:
243 NCDModuleInst_Backend_DeadError(i);
244 }
245  
246 static void ondemand_free (struct ondemand *o)
247 {
248 ASSERT(!o->have_process)
249  
250 // die demands
251 while (!LinkedList1_IsEmpty(&o->demands_list)) {
252 struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node);
253 ASSERT(demand->od == o)
254 demand_free(demand, 0);
255 }
256  
257 NCDModuleInst_Backend_Dead(o->i);
258 }
259  
260 static void ondemand_func_die (void *vo)
261 {
262 struct ondemand *o = vo;
263 ASSERT(!o->dying)
264  
265 // if not have process, die right away
266 if (!o->have_process) {
267 ondemand_free(o);
268 return;
269 }
270  
271 // set dying
272 o->dying = 1;
273  
274 // request process termination if not already
275 if (!o->process_terminating) {
276 ondemand_terminate_process(o);
277 }
278 }
279  
280 static void demand_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
281 {
282 struct demand *o = vo;
283 o->i = i;
284  
285 // read arguments
286 if (!NCDVal_ListRead(params->args, 0)) {
287 ModuleLog(i, BLOG_ERROR, "wrong arity");
288 goto fail0;
289 }
290  
291 // set ondemand
292 o->od = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
293  
294 // add to ondemand's demands list
295 LinkedList1_Append(&o->od->demands_list, &o->demands_list_node);
296  
297 // start process if needed
298 if (!o->od->have_process) {
299 ASSERT(!o->od->dying)
300  
301 if (!ondemand_start_process(o->od)) {
302 goto fail1;
303 }
304 }
305  
306 // if process is up, signal up
307 if (o->od->process_up) {
308 NCDModuleInst_Backend_Up(i);
309 }
310  
311 return;
312  
313 fail1:
314 LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node);
315 fail0:
316 NCDModuleInst_Backend_DeadError(i);
317 }
318  
319 static void demand_free (struct demand *o, int is_error)
320 {
321 // remove from ondemand's demands list
322 LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node);
323  
324 // request process termination if no longer needed
325 if (o->od->have_process && !o->od->process_terminating && LinkedList1_IsEmpty(&o->od->demands_list)) {
326 ondemand_terminate_process(o->od);
327 }
328  
329 if (is_error) {
330 NCDModuleInst_Backend_DeadError(o->i);
331 } else {
332 NCDModuleInst_Backend_Dead(o->i);
333 }
334 }
335  
336 static void demand_func_die (void *vo)
337 {
338 struct demand *o = vo;
339  
340 demand_free(o, 0);
341 }
342  
343 static int demand_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
344 {
345 struct demand *o = vo;
346 ASSERT(o->od->have_process)
347 ASSERT(o->od->process_up)
348  
349 return NCDModuleProcess_GetObj(&o->od->process, objname, out_object);
350 }
351  
352 static struct NCDModule modules[] = {
353 {
354 .type = "ondemand",
355 .func_new2 = ondemand_func_new,
356 .func_die = ondemand_func_die,
357 .alloc_size = sizeof(struct ondemand)
358 }, {
359 .type = "ondemand::demand",
360 .func_new2 = demand_func_new,
361 .func_die = demand_func_die,
362 .func_getobj = demand_func_getobj,
363 .alloc_size = sizeof(struct demand)
364 }, {
365 .type = NULL
366 }
367 };
368  
369 const struct NCDModuleGroup ncdmodule_ondemand = {
370 .modules = modules
371 };