depend.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /**
  2. * @file depend.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. * Dependencies module.
  25. *
  26. * Synopsis: provide(string name)
  27. *
  28. * Synopsis: depend(string name)
  29. * Variables: Provides variables available from the corresponding provide,
  30. * ("modname.varname" or "modname").
  31. */
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <misc/offset.h>
  35. #include <misc/debug.h>
  36. #include <structure/LinkedList2.h>
  37. #include <ncd/NCDModule.h>
  38. #include <generated/blog_channel_ncd_depend.h>
  39. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  40. struct provide {
  41. NCDModuleInst *i;
  42. char *name;
  43. LinkedList2Node provides_node;
  44. LinkedList2 depends;
  45. int dying;
  46. };
  47. struct depend {
  48. NCDModuleInst *i;
  49. char *name;
  50. struct provide *p;
  51. LinkedList2Node node;
  52. };
  53. LinkedList2 provides;
  54. LinkedList2 free_depends;
  55. static struct provide * find_provide (const char *name)
  56. {
  57. LinkedList2Iterator it;
  58. LinkedList2Iterator_InitForward(&it, &provides);
  59. LinkedList2Node *n;
  60. while (n = LinkedList2Iterator_Next(&it)) {
  61. struct provide *p = UPPER_OBJECT(n, struct provide, provides_node);
  62. if (!strcmp(p->name, name)) {
  63. LinkedList2Iterator_Free(&it);
  64. return p;
  65. }
  66. }
  67. return NULL;
  68. }
  69. static int func_globalinit (struct NCDModuleInitParams params)
  70. {
  71. // init provides list
  72. LinkedList2_Init(&provides);
  73. // init free depends list
  74. LinkedList2_Init(&free_depends);
  75. return 1;
  76. }
  77. static void provide_func_new (NCDModuleInst *i)
  78. {
  79. // allocate instance
  80. struct provide *o = malloc(sizeof(*o));
  81. if (!o) {
  82. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  83. goto fail0;
  84. }
  85. NCDModuleInst_Backend_SetUser(i, o);
  86. // init arguments
  87. o->i = i;
  88. // read arguments
  89. NCDValue *name_arg;
  90. if (!NCDValue_ListRead(o->i->args, 1, &name_arg)) {
  91. ModuleLog(i, BLOG_ERROR, "wrong arity");
  92. goto fail1;
  93. }
  94. if (NCDValue_Type(name_arg) != NCDVALUE_STRING) {
  95. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  96. goto fail1;
  97. }
  98. o->name = NCDValue_StringValue(name_arg);
  99. // check for existing provide with this name
  100. if (find_provide(o->name)) {
  101. ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
  102. goto fail1;
  103. }
  104. // insert to provides list
  105. LinkedList2_Append(&provides, &o->provides_node);
  106. // init depends list
  107. LinkedList2_Init(&o->depends);
  108. // set not dying
  109. o->dying = 0;
  110. // signal up.
  111. // This comes above the loop which follows, so that effects on related depend statements are
  112. // computed before this process advances, avoiding problems like failed variable resolutions.
  113. NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
  114. // attach free depends with this name
  115. LinkedList2Iterator it;
  116. LinkedList2Iterator_InitForward(&it, &free_depends);
  117. LinkedList2Node *n;
  118. while (n = LinkedList2Iterator_Next(&it)) {
  119. struct depend *d = UPPER_OBJECT(n, struct depend, node);
  120. ASSERT(!d->p)
  121. if (strcmp(d->name, o->name)) {
  122. continue;
  123. }
  124. // remove from free depends list
  125. LinkedList2_Remove(&free_depends, &d->node);
  126. // insert to provide's list
  127. LinkedList2_Append(&o->depends, &d->node);
  128. // set provide
  129. d->p = o;
  130. // signal up
  131. NCDModuleInst_Backend_Event(d->i, NCDMODULE_EVENT_UP);
  132. }
  133. return;
  134. fail1:
  135. free(o);
  136. fail0:
  137. NCDModuleInst_Backend_SetError(i);
  138. NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
  139. }
  140. static void provide_free (struct provide *o)
  141. {
  142. ASSERT(LinkedList2_IsEmpty(&o->depends))
  143. NCDModuleInst *i = o->i;
  144. // remove from provides list
  145. LinkedList2_Remove(&provides, &o->provides_node);
  146. // free instance
  147. free(o);
  148. NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
  149. }
  150. static void provide_func_die (void *vo)
  151. {
  152. struct provide *o = vo;
  153. ASSERT(!o->dying)
  154. // if we have no depends, die immediately
  155. if (LinkedList2_IsEmpty(&o->depends)) {
  156. provide_free(o);
  157. return;
  158. }
  159. // set dying
  160. o->dying = 1;
  161. // signal our depends down
  162. LinkedList2Iterator it;
  163. LinkedList2Iterator_InitForward(&it, &o->depends);
  164. LinkedList2Node *n;
  165. while (n = LinkedList2Iterator_Next(&it)) {
  166. struct depend *d = UPPER_OBJECT(n, struct depend, node);
  167. ASSERT(d->p == o)
  168. // signal down
  169. NCDModuleInst_Backend_Event(d->i, NCDMODULE_EVENT_DOWN);
  170. }
  171. }
  172. static void depend_func_new (NCDModuleInst *i)
  173. {
  174. // allocate instance
  175. struct depend *o = malloc(sizeof(*o));
  176. if (!o) {
  177. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  178. goto fail0;
  179. }
  180. NCDModuleInst_Backend_SetUser(i, o);
  181. // init arguments
  182. o->i = i;
  183. // read arguments
  184. NCDValue *name_arg;
  185. if (!NCDValue_ListRead(o->i->args, 1, &name_arg)) {
  186. ModuleLog(i, BLOG_ERROR, "wrong arity");
  187. goto fail1;
  188. }
  189. if (NCDValue_Type(name_arg) != NCDVALUE_STRING) {
  190. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  191. goto fail1;
  192. }
  193. o->name = NCDValue_StringValue(name_arg);
  194. // find a provide with our name
  195. struct provide *p = find_provide(o->name);
  196. if (p && !p->dying) {
  197. // insert to provide's list
  198. LinkedList2_Append(&p->depends, &o->node);
  199. // set provide
  200. o->p = p;
  201. // signal up
  202. NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
  203. } else {
  204. // insert to free depends list
  205. LinkedList2_Append(&free_depends, &o->node);
  206. // set no provide
  207. o->p = NULL;
  208. }
  209. return;
  210. fail1:
  211. free(o);
  212. fail0:
  213. NCDModuleInst_Backend_SetError(i);
  214. NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
  215. }
  216. static void depend_free (struct depend *o)
  217. {
  218. NCDModuleInst *i = o->i;
  219. if (o->p) {
  220. // remove from provide's list
  221. LinkedList2_Remove(&o->p->depends, &o->node);
  222. // if provide is dying and is empty, let it die
  223. if (o->p->dying && LinkedList2_IsEmpty(&o->p->depends)) {
  224. provide_free(o->p);
  225. }
  226. } else {
  227. // remove free depends list
  228. LinkedList2_Remove(&free_depends, &o->node);
  229. }
  230. // free instance
  231. free(o);
  232. NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
  233. }
  234. static void depend_func_die (void *vo)
  235. {
  236. struct depend *o = vo;
  237. depend_free(o);
  238. }
  239. static void depend_func_clean (void *vo)
  240. {
  241. struct depend *o = vo;
  242. if (!(o->p && o->p->dying)) {
  243. return;
  244. }
  245. struct provide *p = o->p;
  246. // remove from provide's list
  247. LinkedList2_Remove(&p->depends, &o->node);
  248. // insert to free depends list
  249. LinkedList2_Append(&free_depends, &o->node);
  250. // set no provide
  251. o->p = NULL;
  252. // if provide is empty, let it die
  253. if (LinkedList2_IsEmpty(&p->depends)) {
  254. provide_free(p);
  255. }
  256. }
  257. static int depend_func_getvar (void *vo, const char *name_orig, NCDValue *out)
  258. {
  259. struct depend *o = vo;
  260. ASSERT(o->p)
  261. ASSERT(!o->p->dying)
  262. int ret = 0;
  263. char *name = strdup(name_orig);
  264. if (!name) {
  265. ModuleLog(o->i, BLOG_ERROR, "strdup failed");
  266. goto fail0;
  267. }
  268. const char *modname;
  269. const char *varname;
  270. char *dot = strstr(name, ".");
  271. if (!dot) {
  272. modname = name;
  273. varname = "";
  274. } else {
  275. *dot = '\0';
  276. modname = name;
  277. varname = dot + 1;
  278. }
  279. ret = NCDModuleInst_Backend_GetVar(o->p->i, modname, varname, out);
  280. free(name);
  281. fail0:
  282. return ret;
  283. }
  284. static const struct NCDModule modules[] = {
  285. {
  286. .type = "provide",
  287. .func_new = provide_func_new,
  288. .func_die = provide_func_die
  289. }, {
  290. .type = "depend",
  291. .func_new = depend_func_new,
  292. .func_die = depend_func_die,
  293. .func_clean = depend_func_clean,
  294. .func_getvar = depend_func_getvar
  295. }, {
  296. .type = NULL
  297. }
  298. };
  299. const struct NCDModuleGroup ncdmodule_depend = {
  300. .func_globalinit = func_globalinit,
  301. .modules = modules
  302. };