rimp_call.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /**
  2. * @file rimp_call.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * This file is part of BadVPN.
  8. *
  9. * BadVPN is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2
  11. * as published by the Free Software Foundation.
  12. *
  13. * BadVPN is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. *
  22. * @section DESCRIPTION
  23. *
  24. * Reverse imperative call.
  25. *
  26. * Synopsis:
  27. * rimp_call(string template_name, list args)
  28. * rimp_call_timeout(string template_name, list args, string timeout_ms)
  29. *
  30. * Description:
  31. * Goes up immediately. On deinitialization, does the following, in order:
  32. * 1. starts a template process from the given template and arguments
  33. * and waits for it to completely initialize, or for the timeout to
  34. * elapse, then
  35. * 2. requests termination of the process and waits for it to terminate,
  36. * then finally
  37. * 3. deinitializes.
  38. *
  39. * WARNING: if there's a bug in the NCD program and the started template
  40. * process never initializes completely, rimp_call() will never
  41. * terminate, and you will have to kill the NCD process by force.
  42. */
  43. #include <stdlib.h>
  44. #include <misc/parse_number.h>
  45. #include <ncd/NCDModule.h>
  46. #include <generated/blog_channel_ncd_rimp_call.h>
  47. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  48. #define STATE_UP 1
  49. #define STATE_WORKING 2
  50. #define STATE_CLEANING 3
  51. struct instance {
  52. NCDModuleInst *i;
  53. char *template_name;
  54. NCDValue *args;
  55. int have_timeout;
  56. BTimer timer;
  57. NCDModuleProcess process;
  58. int state;
  59. };
  60. static void instance_free (struct instance *o);
  61. static void process_handler_event (struct instance *o, int event)
  62. {
  63. switch (event) {
  64. case NCDMODULEPROCESS_EVENT_UP: {
  65. ASSERT(o->state == STATE_WORKING)
  66. // stop timer
  67. if (o->have_timeout) {
  68. BReactor_RemoveTimer(o->i->reactor, &o->timer);
  69. }
  70. // start terminating
  71. NCDModuleProcess_Terminate(&o->process);
  72. // set state cleaning
  73. o->state = STATE_CLEANING;
  74. } break;
  75. case NCDMODULEPROCESS_EVENT_TERMINATED: {
  76. ASSERT(o->state == STATE_CLEANING)
  77. // free process
  78. NCDModuleProcess_Free(&o->process);
  79. // free instance
  80. instance_free(o);
  81. return;
  82. } break;
  83. default: ASSERT(0);
  84. }
  85. }
  86. static void timer_handler (struct instance *o)
  87. {
  88. ASSERT(o->have_timeout)
  89. ASSERT(o->state == STATE_WORKING)
  90. ModuleLog(o->i, BLOG_ERROR, "rimp_call timeout elapsed");
  91. // start terminating
  92. NCDModuleProcess_Terminate(&o->process);
  93. // set state cleaning
  94. o->state = STATE_CLEANING;
  95. }
  96. static void func_new (NCDModuleInst *i)
  97. {
  98. // allocate instance
  99. struct instance *o = malloc(sizeof(*o));
  100. if (!o) {
  101. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  102. goto fail0;
  103. }
  104. NCDModuleInst_Backend_SetUser(i, o);
  105. // init arguments
  106. o->i = i;
  107. // check arguments
  108. NCDValue *template_name_arg;
  109. if (!NCDValue_ListRead(i->args, 2, &template_name_arg, &o->args)) {
  110. ModuleLog(i, BLOG_ERROR, "wrong arity");
  111. goto fail1;
  112. }
  113. if (NCDValue_Type(template_name_arg) != NCDVALUE_STRING || NCDValue_Type(o->args) != NCDVALUE_LIST) {
  114. ModuleLog(i, BLOG_ERROR, "wrong type");
  115. goto fail1;
  116. }
  117. o->template_name = NCDValue_StringValue(template_name_arg);
  118. // set have no timeout
  119. o->have_timeout = 0;
  120. // signal up
  121. NCDModuleInst_Backend_Up(i);
  122. // set state up
  123. o->state = STATE_UP;
  124. return;
  125. fail1:
  126. free(o);
  127. fail0:
  128. NCDModuleInst_Backend_SetError(i);
  129. NCDModuleInst_Backend_Dead(i);
  130. }
  131. static void func_new_timeout (NCDModuleInst *i)
  132. {
  133. // allocate instance
  134. struct instance *o = malloc(sizeof(*o));
  135. if (!o) {
  136. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  137. goto fail0;
  138. }
  139. NCDModuleInst_Backend_SetUser(i, o);
  140. // init arguments
  141. o->i = i;
  142. // check arguments
  143. NCDValue *template_name_arg;
  144. NCDValue *timeout_arg;
  145. if (!NCDValue_ListRead(i->args, 3, &template_name_arg, &o->args, &timeout_arg)) {
  146. ModuleLog(i, BLOG_ERROR, "wrong arity");
  147. goto fail1;
  148. }
  149. if (NCDValue_Type(template_name_arg) != NCDVALUE_STRING || NCDValue_Type(o->args) != NCDVALUE_LIST ||
  150. NCDValue_Type(timeout_arg) != NCDVALUE_STRING) {
  151. ModuleLog(i, BLOG_ERROR, "wrong type");
  152. goto fail1;
  153. }
  154. o->template_name = NCDValue_StringValue(template_name_arg);
  155. // parse timeout
  156. uintmax_t timeout;
  157. if (!parse_unsigned_integer(NCDValue_StringValue(timeout_arg), &timeout) || timeout > UINT64_MAX) {
  158. ModuleLog(i, BLOG_ERROR, "wrong timeout");
  159. goto fail1;
  160. }
  161. // set have timeout
  162. o->have_timeout = 1;
  163. // init timer
  164. BTimer_Init(&o->timer, timeout, (BTimer_handler)timer_handler, o);
  165. // signal up
  166. NCDModuleInst_Backend_Up(i);
  167. // set state up
  168. o->state = STATE_UP;
  169. return;
  170. fail1:
  171. free(o);
  172. fail0:
  173. NCDModuleInst_Backend_SetError(i);
  174. NCDModuleInst_Backend_Dead(i);
  175. }
  176. void instance_free (struct instance *o)
  177. {
  178. NCDModuleInst *i = o->i;
  179. // free instance
  180. free(o);
  181. NCDModuleInst_Backend_Dead(i);
  182. }
  183. static void func_die (void *vo)
  184. {
  185. struct instance *o = vo;
  186. ASSERT(o->state == STATE_UP)
  187. // copy arguments
  188. NCDValue args;
  189. if (!NCDValue_InitCopy(&args, o->args)) {
  190. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  191. goto fail;
  192. }
  193. // create process
  194. if (!NCDModuleProcess_Init(&o->process, o->i, o->template_name, args, o, (NCDModuleProcess_handler_event)process_handler_event)) {
  195. ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
  196. NCDValue_Free(&args);
  197. goto fail;
  198. }
  199. // start timer if used
  200. if (o->have_timeout) {
  201. BReactor_SetTimer(o->i->reactor, &o->timer);
  202. }
  203. // set state working
  204. o->state = STATE_WORKING;
  205. return;
  206. fail:
  207. instance_free(o);
  208. }
  209. static const struct NCDModule modules[] = {
  210. {
  211. .type = "rimp_call",
  212. .func_new = func_new,
  213. .func_die = func_die
  214. }, {
  215. .type = "rimp_call_timeout",
  216. .func_new = func_new_timeout,
  217. .func_die = func_die
  218. }, {
  219. .type = NULL
  220. }
  221. };
  222. const struct NCDModuleGroup ncdmodule_rimp_call = {
  223. .modules = modules
  224. };