sys_request_client.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /**
  2. * @file sys_request_client.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. * Synopsis:
  32. * sys.request_client(string socket_path)
  33. *
  34. * Description:
  35. * Connects to a request server (sys.request_server()) over a Unix socket.
  36. * Goes up when the connection, and dies with error when it is broken.
  37. * When requested to die, dies immediately, breaking the connection.
  38. *
  39. * Synopsis:
  40. * sys.request_client::request(request_data, string reply_handler, string finished_handler, list args)
  41. *
  42. * Description:
  43. * Sends a request to the server and dispatches replies to the provided handlers.
  44. *
  45. * The 'request_data' argument is sent as part of the request and is used by the server
  46. * to determine what to do with the request.
  47. *
  48. * When a reply is received, a new template process is created from 'reply_handler' to process the
  49. * reply. This process can access the reply data sent by the server using '_reply.data'.
  50. * Similarly, if the server finishes the request, a process is created from 'finished_handler'.
  51. * In both cases, the process can access objects as seen from the request statement via "_caller".
  52. * Termination of these processes is initiated immediately after they completes. They are created
  53. * synchronously - if a reply or a finished message arrives before a previous process is has
  54. * finished, it is queued. Once the finished message has been processed by 'finished_handler', no
  55. * more processes will be created.
  56. *
  57. * When the request statement is requested to terminate, it initiates termination of the current
  58. * handler process and waits for it to terminate (if any is running), and then dies.
  59. * If the corresponding client statement dies after being requested to die, or as a result of
  60. * an error, the request statement will not react to this. It will dispatch any pending messages
  61. * and then proceed to do nothing. In this case, if a finished message was not received, it will
  62. * not be dispatched.
  63. *
  64. * The request statement may however die at any time due to errors. In this case, it will
  65. * initiate termination of the current process and wait for it to terminate (if any) before dying.
  66. *
  67. * The request protocol and the server allow the client the abort requests at any time, and to
  68. * have the client notified only after the request has been completely aborted (i.e. the handler
  69. * process of sys.request_server() has deinitialized completely). This client implementation will
  70. * automatically request abortion of active requests when the request statement is requested
  71. * to die. However, the request statement will not wait for the abortion to finish before dying.
  72. * This means, for instance, that if you initialize a request statement right after having
  73. * deinitiazed it, the requests may overlap on the server side.
  74. */
  75. #include <stdlib.h>
  76. #include <string.h>
  77. #include <misc/offset.h>
  78. #include <structure/LinkedList0.h>
  79. #include <structure/LinkedList1.h>
  80. #include <ncd/NCDModule.h>
  81. #include <ncd/NCDRequestClient.h>
  82. #include <generated/blog_channel_ncd_sys_request_client.h>
  83. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  84. #define CSTATE_CONNECTING 1
  85. #define CSTATE_CONNECTED 2
  86. #define RRSTATE_SENDING_REQUEST 1
  87. #define RRSTATE_READY 2
  88. #define RRSTATE_GONE_BAD 3
  89. #define RRSTATE_GONE_GOOD 4
  90. #define RPSTATE_NONE 1
  91. #define RPSTATE_WORKING 2
  92. #define RPSTATE_TERMINATING 3
  93. #define RDSTATE_NONE 1
  94. #define RDSTATE_DYING 2
  95. #define RDSTATE_DYING_ERROR 3
  96. struct instance {
  97. NCDModuleInst *i;
  98. NCDRequestClient client;
  99. LinkedList0 requests_list;
  100. int state;
  101. };
  102. struct request_instance {
  103. NCDModuleInst *i;
  104. char *reply_handler;
  105. char *finished_handler;
  106. NCDValue *args;
  107. struct instance *client;
  108. NCDRequestClientRequest request;
  109. LinkedList0Node requests_list_node;
  110. LinkedList1 replies_list;
  111. NCDModuleProcess process;
  112. int process_is_finished;
  113. NCDValue process_reply_data;
  114. int rstate;
  115. int pstate;
  116. int dstate;
  117. };
  118. struct reply {
  119. LinkedList1Node replies_list_node;
  120. NCDValue val;
  121. };
  122. static void client_handler_error (struct instance *o);
  123. static void client_handler_connected (struct instance *o);
  124. static void request_handler_sent (struct request_instance *o);
  125. static void request_handler_reply (struct request_instance *o, NCDValue reply_data);
  126. static void request_handler_finished (struct request_instance *o, int is_error);
  127. static void request_process_handler_event (struct request_instance *o, int event);
  128. static int request_process_func_getspecialobj (struct request_instance *o, const char *name, NCDObject *out_object);
  129. static int request_process_caller_obj_func_getobj (struct request_instance *o, const char *name, NCDObject *out_object);
  130. static int request_process_reply_obj_func_getvar (struct request_instance *o, const char *name, NCDValue *out_value);
  131. static void request_gone (struct request_instance *o, int is_bad);
  132. static void request_terminate_process (struct request_instance *o);
  133. static void request_die (struct request_instance *o, int is_error);
  134. static void request_free_reply (struct request_instance *o, struct reply *r, int have_value);
  135. static int request_init_reply_process (struct request_instance *o, NCDValue reply_data);
  136. static int request_init_finished_process (struct request_instance *o);
  137. static void instance_free (struct instance *o, int with_error);
  138. static void request_instance_free (struct request_instance *o, int with_error);
  139. static void client_handler_error (struct instance *o)
  140. {
  141. ModuleLog(o->i, BLOG_ERROR, "client error");
  142. // free instance
  143. instance_free(o, 1);
  144. }
  145. static void client_handler_connected (struct instance *o)
  146. {
  147. ASSERT(o->state == CSTATE_CONNECTING)
  148. // signal up
  149. NCDModuleInst_Backend_Up(o->i);
  150. // set state connected
  151. o->state = CSTATE_CONNECTED;
  152. }
  153. static void request_handler_sent (struct request_instance *o)
  154. {
  155. ASSERT(o->rstate == RRSTATE_SENDING_REQUEST)
  156. // signal up
  157. NCDModuleInst_Backend_Up(o->i);
  158. // set rstate ready
  159. o->rstate = RRSTATE_READY;
  160. }
  161. static void request_handler_reply (struct request_instance *o, NCDValue reply_data)
  162. {
  163. ASSERT(o->rstate == RRSTATE_READY)
  164. // queue reply if process is running
  165. if (o->pstate != RPSTATE_NONE) {
  166. struct reply *r = malloc(sizeof(*r));
  167. if (!r) {
  168. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  169. goto fail;
  170. }
  171. r->val = reply_data;
  172. LinkedList1_Append(&o->replies_list, &r->replies_list_node);
  173. return;
  174. }
  175. // start reply process
  176. if (!request_init_reply_process(o, reply_data)) {
  177. goto fail;
  178. }
  179. return;
  180. fail:
  181. NCDValue_Free(&reply_data);
  182. request_die(o, 1);
  183. }
  184. static void request_handler_finished (struct request_instance *o, int is_error)
  185. {
  186. ASSERT(o->rstate == RRSTATE_SENDING_REQUEST || o->rstate == RRSTATE_READY)
  187. ASSERT(is_error || o->rstate == RRSTATE_READY)
  188. if (is_error) {
  189. ModuleLog(o->i, BLOG_ERROR, "received error reply");
  190. goto fail;
  191. }
  192. // request gone good
  193. request_gone(o, 0);
  194. // start process for reporting finished, if possible
  195. if (o->pstate == RPSTATE_NONE) {
  196. if (!request_init_finished_process(o)) {
  197. goto fail;
  198. }
  199. }
  200. return;
  201. fail:
  202. request_die(o, 1);
  203. }
  204. static void request_process_handler_event (struct request_instance *o, int event)
  205. {
  206. ASSERT(o->pstate != RPSTATE_NONE)
  207. switch (event) {
  208. case NCDMODULEPROCESS_EVENT_UP: {
  209. ASSERT(o->pstate == RPSTATE_WORKING)
  210. // request process termination
  211. request_terminate_process(o);
  212. } break;
  213. case NCDMODULEPROCESS_EVENT_DOWN: {
  214. ASSERT(0)
  215. } break;
  216. case NCDMODULEPROCESS_EVENT_TERMINATED: {
  217. ASSERT(o->pstate == RPSTATE_TERMINATING)
  218. ASSERT(o->rstate != RRSTATE_SENDING_REQUEST)
  219. // free process
  220. NCDModuleProcess_Free(&o->process);
  221. // free reply data
  222. if (!o->process_is_finished) {
  223. NCDValue_Free(&o->process_reply_data);
  224. }
  225. // set process state none
  226. o->pstate = RPSTATE_NONE;
  227. // die finally if requested
  228. if (o->dstate == RDSTATE_DYING || o->dstate == RDSTATE_DYING_ERROR) {
  229. request_instance_free(o, o->dstate == RDSTATE_DYING_ERROR);
  230. return;
  231. }
  232. if (!LinkedList1_IsEmpty(&o->replies_list)) {
  233. // get first reply
  234. struct reply *r = UPPER_OBJECT(LinkedList1_GetFirst(&o->replies_list), struct reply, replies_list_node);
  235. // start reply process
  236. if (!request_init_reply_process(o, r->val)) {
  237. goto fail;
  238. }
  239. // free reply
  240. request_free_reply(o, r, 0);
  241. }
  242. else if (o->rstate == RRSTATE_GONE_GOOD && !o->process_is_finished) {
  243. // start process for reporting finished
  244. if (!request_init_finished_process(o)) {
  245. goto fail;
  246. }
  247. }
  248. return;
  249. fail:
  250. request_die(o, 1);
  251. } break;
  252. }
  253. }
  254. static int request_process_func_getspecialobj (struct request_instance *o, const char *name, NCDObject *out_object)
  255. {
  256. ASSERT(o->pstate != RPSTATE_NONE)
  257. if (!strcmp(name, "_caller")) {
  258. *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)request_process_caller_obj_func_getobj);
  259. return 1;
  260. }
  261. if (!o->process_is_finished && !strcmp(name, "_reply")) {
  262. *out_object = NCDObject_Build(NULL, o, (NCDObject_func_getvar)request_process_reply_obj_func_getvar, NULL);
  263. return 1;
  264. }
  265. return 0;
  266. }
  267. static int request_process_caller_obj_func_getobj (struct request_instance *o, const char *name, NCDObject *out_object)
  268. {
  269. ASSERT(o->pstate != RPSTATE_NONE)
  270. return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
  271. }
  272. static int request_process_reply_obj_func_getvar (struct request_instance *o, const char *name, NCDValue *out_value)
  273. {
  274. ASSERT(o->pstate != RPSTATE_NONE)
  275. ASSERT(!o->process_is_finished)
  276. if (!strcmp(name, "data")) {
  277. if (!NCDValue_InitCopy(out_value, &o->process_reply_data)) {
  278. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  279. return 0;
  280. }
  281. return 1;
  282. }
  283. return 0;
  284. }
  285. static void request_gone (struct request_instance *o, int is_bad)
  286. {
  287. ASSERT(o->rstate != RRSTATE_GONE_BAD)
  288. ASSERT(o->rstate != RRSTATE_GONE_GOOD)
  289. // remove from requests list
  290. LinkedList0_Remove(&o->client->requests_list, &o->requests_list_node);
  291. // free request
  292. NCDRequestClientRequest_Free(&o->request);
  293. // set state over
  294. o->rstate = (is_bad ? RRSTATE_GONE_BAD : RRSTATE_GONE_GOOD);
  295. }
  296. static void request_terminate_process (struct request_instance *o)
  297. {
  298. ASSERT(o->pstate == RPSTATE_WORKING)
  299. // request process termination
  300. NCDModuleProcess_Terminate(&o->process);
  301. // set process state terminating
  302. o->pstate = RPSTATE_TERMINATING;
  303. }
  304. static void request_die (struct request_instance *o, int is_error)
  305. {
  306. // if we have no process, die right away, else we have to wait for process to terminate
  307. if (o->pstate == RPSTATE_NONE) {
  308. request_instance_free(o, is_error);
  309. return;
  310. }
  311. // release request
  312. if (o->rstate != RRSTATE_GONE_BAD && o->rstate != RRSTATE_GONE_GOOD) {
  313. request_gone(o, 1);
  314. }
  315. // initiate process termination, if needed
  316. if (o->pstate != RPSTATE_TERMINATING) {
  317. request_terminate_process(o);
  318. }
  319. // set dstate
  320. o->dstate = (is_error ? RDSTATE_DYING_ERROR : RDSTATE_DYING);
  321. }
  322. static void request_free_reply (struct request_instance *o, struct reply *r, int have_value)
  323. {
  324. // remove from replies list
  325. LinkedList1_Remove(&o->replies_list, &r->replies_list_node);
  326. // free value
  327. if (have_value) {
  328. NCDValue_Free(&r->val);
  329. }
  330. // free structure
  331. free(r);
  332. }
  333. static int request_init_reply_process (struct request_instance *o, NCDValue reply_data)
  334. {
  335. ASSERT(o->pstate == RPSTATE_NONE)
  336. // set parameters
  337. o->process_is_finished = 0;
  338. o->process_reply_data = reply_data;
  339. // copy arguments
  340. NCDValue args;
  341. if (!NCDValue_InitCopy(&args, o->args)) {
  342. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  343. goto fail0;
  344. }
  345. // init process
  346. if (!NCDModuleProcess_Init(&o->process, o->i, o->reply_handler, args, o, (NCDModuleProcess_handler_event)request_process_handler_event)) {
  347. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  348. NCDValue_Free(&args);
  349. goto fail0;
  350. }
  351. // set special objects function
  352. NCDModuleProcess_SetSpecialFuncs(&o->process, (NCDModuleProcess_func_getspecialobj)request_process_func_getspecialobj);
  353. // set process state working
  354. o->pstate = RPSTATE_WORKING;
  355. return 1;
  356. fail0:
  357. return 0;
  358. }
  359. static int request_init_finished_process (struct request_instance *o)
  360. {
  361. ASSERT(o->pstate == RPSTATE_NONE)
  362. // set parameters
  363. o->process_is_finished = 1;
  364. // copy arguments
  365. NCDValue args;
  366. if (!NCDValue_InitCopy(&args, o->args)) {
  367. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  368. goto fail0;
  369. }
  370. // init process
  371. if (!NCDModuleProcess_Init(&o->process, o->i, o->finished_handler, args, o, (NCDModuleProcess_handler_event)request_process_handler_event)) {
  372. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  373. NCDValue_Free(&args);
  374. goto fail0;
  375. }
  376. // set special objects function
  377. NCDModuleProcess_SetSpecialFuncs(&o->process, (NCDModuleProcess_func_getspecialobj)request_process_func_getspecialobj);
  378. // set process state working
  379. o->pstate = RPSTATE_WORKING;
  380. return 1;
  381. fail0:
  382. return 0;
  383. }
  384. static void func_new (NCDModuleInst *i)
  385. {
  386. // allocate structure
  387. struct instance *o = malloc(sizeof(*o));
  388. if (!o) {
  389. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  390. goto fail0;
  391. }
  392. o->i = i;
  393. NCDModuleInst_Backend_SetUser(i, o);
  394. // check arguments
  395. NCDValue *socket_path_arg;
  396. if (!NCDValue_ListRead(i->args, 1, &socket_path_arg)) {
  397. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  398. goto fail1;
  399. }
  400. if (NCDValue_Type(socket_path_arg) != NCDVALUE_STRING) {
  401. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  402. goto fail1;
  403. }
  404. char *socket_path = NCDValue_StringValue(socket_path_arg);
  405. // init client
  406. if (!NCDRequestClient_Init(&o->client, NCDREQUESTCLIENT_UNIX_ADDR(socket_path), i->params->reactor, o,
  407. (NCDRequestClient_handler_error)client_handler_error,
  408. (NCDRequestClient_handler_connected)client_handler_connected)) {
  409. ModuleLog(o->i, BLOG_ERROR, "NCDRequestClient_Init failed");
  410. goto fail1;
  411. }
  412. // init requests list
  413. LinkedList0_Init(&o->requests_list);
  414. // set state connecting
  415. o->state = CSTATE_CONNECTING;
  416. return;
  417. fail1:
  418. free(o);
  419. fail0:
  420. NCDModuleInst_Backend_SetError(i);
  421. NCDModuleInst_Backend_Dead(i);
  422. }
  423. static void instance_free (struct instance *o, int with_error)
  424. {
  425. NCDModuleInst *i = o->i;
  426. // deal with requests
  427. LinkedList0Node *ln;
  428. while (ln = LinkedList0_GetFirst(&o->requests_list)) {
  429. struct request_instance *req = UPPER_OBJECT(ln, struct request_instance, requests_list_node);
  430. request_gone(req, 1);
  431. }
  432. // free client
  433. NCDRequestClient_Free(&o->client);
  434. // free structure
  435. free(o);
  436. if (with_error) {
  437. NCDModuleInst_Backend_SetError(i);
  438. }
  439. NCDModuleInst_Backend_Dead(i);
  440. }
  441. static void func_die (void *vo)
  442. {
  443. struct instance *o = vo;
  444. instance_free(o, 0);
  445. }
  446. static void request_func_new (NCDModuleInst *i)
  447. {
  448. // allocate structure
  449. struct request_instance *o = malloc(sizeof(*o));
  450. if (!o) {
  451. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  452. goto fail0;
  453. }
  454. o->i = i;
  455. NCDModuleInst_Backend_SetUser(i, o);
  456. // check arguments
  457. NCDValue *request_data_arg;
  458. NCDValue *reply_handler_arg;
  459. NCDValue *finished_handler_arg;
  460. NCDValue *args_arg;
  461. if (!NCDValue_ListRead(i->args, 4, &request_data_arg, &reply_handler_arg, &finished_handler_arg, &args_arg)) {
  462. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  463. goto fail1;
  464. }
  465. if (NCDValue_Type(reply_handler_arg) != NCDVALUE_STRING || NCDValue_Type(finished_handler_arg) != NCDVALUE_STRING ||
  466. NCDValue_Type(args_arg) != NCDVALUE_LIST
  467. ) {
  468. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  469. goto fail1;
  470. }
  471. o->reply_handler = NCDValue_StringValue(reply_handler_arg);
  472. o->finished_handler = NCDValue_StringValue(finished_handler_arg);
  473. o->args = args_arg;
  474. // get client
  475. struct instance *client = ((NCDModuleInst *)i->method_user)->inst_user;
  476. o->client = client;
  477. // check client state
  478. if (client->state != CSTATE_CONNECTED) {
  479. ModuleLog(o->i, BLOG_ERROR, "client is not connected");
  480. goto fail1;
  481. }
  482. // init request
  483. if (!NCDRequestClientRequest_Init(&o->request, &client->client, request_data_arg, o,
  484. (NCDRequestClientRequest_handler_sent)request_handler_sent,
  485. (NCDRequestClientRequest_handler_reply)request_handler_reply,
  486. (NCDRequestClientRequest_handler_finished)request_handler_finished)) {
  487. ModuleLog(o->i, BLOG_ERROR, "NCDRequestClientRequest_Init failed");
  488. goto fail1;
  489. }
  490. // add to requests list
  491. LinkedList0_Prepend(&client->requests_list, &o->requests_list_node);
  492. // init replies list
  493. LinkedList1_Init(&o->replies_list);
  494. // set state
  495. o->rstate = RRSTATE_SENDING_REQUEST;
  496. o->pstate = RPSTATE_NONE;
  497. o->dstate = RDSTATE_NONE;
  498. return;
  499. fail1:
  500. free(o);
  501. fail0:
  502. NCDModuleInst_Backend_SetError(i);
  503. NCDModuleInst_Backend_Dead(i);
  504. }
  505. static void request_instance_free (struct request_instance *o, int with_error)
  506. {
  507. ASSERT(o->pstate == RPSTATE_NONE)
  508. NCDModuleInst *i = o->i;
  509. // free replies
  510. LinkedList1Node *ln;
  511. while (ln = LinkedList1_GetFirst(&o->replies_list)) {
  512. struct reply *r = UPPER_OBJECT(ln, struct reply, replies_list_node);
  513. request_free_reply(o, r, 1);
  514. }
  515. // release request
  516. if (o->rstate != RRSTATE_GONE_BAD && o->rstate != RRSTATE_GONE_GOOD) {
  517. request_gone(o, 1);
  518. }
  519. // free structure
  520. free(o);
  521. if (with_error) {
  522. NCDModuleInst_Backend_SetError(i);
  523. }
  524. NCDModuleInst_Backend_Dead(i);
  525. }
  526. static void request_func_die (void *vo)
  527. {
  528. struct request_instance *o = vo;
  529. request_die(o, 0);
  530. }
  531. static const struct NCDModule modules[] = {
  532. {
  533. .type = "sys.request_client",
  534. .func_new = func_new,
  535. .func_die = func_die
  536. }, {
  537. .type = "sys.request_client::request",
  538. .func_new = request_func_new,
  539. .func_die = request_func_die
  540. }, {
  541. .type = NULL
  542. }
  543. };
  544. const struct NCDModuleGroup ncdmodule_sys_request_client = {
  545. .modules = modules
  546. };