ncd.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. /**
  2. * @file ncd.c
  3. * @author Ambroz Bizjak <[email protected]>
  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. #include <stdint.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #include <misc/version.h>
  27. #include <misc/loggers_string.h>
  28. #include <misc/loglevel.h>
  29. #include <misc/offset.h>
  30. #include <misc/read_file.h>
  31. #include <misc/balloc.h>
  32. #include <misc/concat_strings.h>
  33. #include <structure/LinkedList2.h>
  34. #include <system/BLog.h>
  35. #include <system/BReactor.h>
  36. #include <system/BSignal.h>
  37. #include <system/BSocket.h>
  38. #include <process/BProcess.h>
  39. #include <ncdconfig/NCDConfigParser.h>
  40. #include <ncd/NCDModule.h>
  41. #include <ncd/modules/modules.h>
  42. #ifndef BADVPN_USE_WINAPI
  43. #include <system/BLog_syslog.h>
  44. #endif
  45. #include <ncd/ncd.h>
  46. #include <generated/blog_channel_ncd.h>
  47. #define LOGGER_STDOUT 1
  48. #define LOGGER_SYSLOG 2
  49. #define SSTATE_CHILD 1
  50. #define SSTATE_ADULT 2
  51. #define SSTATE_DYING 3
  52. #define SSTATE_FORGOTTEN 4
  53. struct statement {
  54. char *object_name;
  55. char *method_name;
  56. struct argument_elem *first_arg;
  57. char *name;
  58. };
  59. struct argument_elem {
  60. int is_var;
  61. union {
  62. struct {
  63. char *modname;
  64. char *varname;
  65. } var;
  66. NCDValue val;
  67. };
  68. struct argument_elem *next_arg;
  69. };
  70. struct process {
  71. char *name;
  72. size_t num_statements;
  73. struct process_statement *statements;
  74. size_t ap;
  75. size_t fp;
  76. BTimer wait_timer;
  77. BPending advance_job;
  78. BPending work_job;
  79. LinkedList2Node list_node; // node in processes
  80. };
  81. struct process_statement {
  82. struct process *p;
  83. size_t i;
  84. struct statement s;
  85. int state;
  86. char *type;
  87. int have_error;
  88. btime_t error_until;
  89. NCDModuleInst inst;
  90. NCDValue inst_args;
  91. char logprefix[50];
  92. };
  93. // command-line options
  94. struct {
  95. int help;
  96. int version;
  97. int logger;
  98. #ifndef BADVPN_USE_WINAPI
  99. char *logger_syslog_facility;
  100. char *logger_syslog_ident;
  101. #endif
  102. int loglevel;
  103. int loglevels[BLOG_NUM_CHANNELS];
  104. char *config_file;
  105. int retry_time;
  106. } options;
  107. // reactor
  108. BReactor ss;
  109. // are we terminating
  110. int terminating;
  111. // process manager
  112. BProcessManager manager;
  113. // config AST
  114. struct NCDConfig_interfaces *config_ast;
  115. // processes
  116. LinkedList2 processes;
  117. static void print_help (const char *name);
  118. static void print_version (void);
  119. static int parse_arguments (int argc, char *argv[]);
  120. static void signal_handler (void *unused);
  121. static const struct NCDModule * find_module (const char *name);
  122. static int statement_init (struct statement *s, struct NCDConfig_statements *conf);
  123. static void statement_free (struct statement *s);
  124. static void statement_free_args (struct statement *s);
  125. static int process_new (struct NCDConfig_interfaces *conf);
  126. static void process_free (struct process *p);
  127. static void process_free_statements (struct process *p);
  128. static size_t process_rap (struct process *p);
  129. static void process_assert_pointers (struct process *p);
  130. static void process_log (struct process *p, int level, const char *fmt, ...);
  131. static void process_schedule_work (struct process *p);
  132. static void process_work_job_handler (struct process *p);
  133. static void process_advance_job_handler (struct process *p);
  134. static void process_wait_timer_handler (struct process *p);
  135. static struct process_statement * process_find_statement (struct process *p, size_t pos, const char *name);
  136. static int process_resolve_variable (struct process *p, size_t pos, const char *modname, const char *varname, NCDValue *out);
  137. static struct process_statement * process_resolve_object (struct process *p, size_t pos, const char *objname);
  138. static void process_statement_log (struct process_statement *ps, int level, const char *fmt, ...);
  139. static void process_statement_set_error (struct process_statement *ps);
  140. static void process_statement_instance_handler_event (struct process_statement *ps, int event);
  141. static int process_statement_instance_handler_getvar (struct process_statement *ps, const char *modname, const char *varname, NCDValue *out);
  142. static NCDModuleInst * process_statement_instance_handler_getobj (struct process_statement *ps, const char *objname);
  143. int main (int argc, char **argv)
  144. {
  145. if (argc <= 0) {
  146. return 1;
  147. }
  148. // parse command-line arguments
  149. if (!parse_arguments(argc, argv)) {
  150. fprintf(stderr, "Failed to parse arguments\n");
  151. print_help(argv[0]);
  152. goto fail0;
  153. }
  154. // handle --help and --version
  155. if (options.help) {
  156. print_version();
  157. print_help(argv[0]);
  158. return 0;
  159. }
  160. if (options.version) {
  161. print_version();
  162. return 0;
  163. }
  164. // initialize logger
  165. switch (options.logger) {
  166. case LOGGER_STDOUT:
  167. BLog_InitStdout();
  168. break;
  169. #ifndef BADVPN_USE_WINAPI
  170. case LOGGER_SYSLOG:
  171. if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) {
  172. fprintf(stderr, "Failed to initialize syslog logger\n");
  173. goto fail0;
  174. }
  175. break;
  176. #endif
  177. default:
  178. ASSERT(0);
  179. }
  180. // configure logger channels
  181. for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
  182. if (options.loglevels[i] >= 0) {
  183. BLog_SetChannelLoglevel(i, options.loglevels[i]);
  184. }
  185. else if (options.loglevel >= 0) {
  186. BLog_SetChannelLoglevel(i, options.loglevel);
  187. }
  188. }
  189. BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
  190. // initialize sockets
  191. if (BSocket_GlobalInit() < 0) {
  192. BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
  193. goto fail1;
  194. }
  195. // init time
  196. BTime_Init();
  197. // init reactor
  198. if (!BReactor_Init(&ss)) {
  199. BLog(BLOG_ERROR, "BReactor_Init failed");
  200. goto fail1;
  201. }
  202. // set not terminating
  203. terminating = 0;
  204. // init process manager
  205. if (!BProcessManager_Init(&manager, &ss)) {
  206. BLog(BLOG_ERROR, "BProcessManager_Init failed");
  207. goto fail1a;
  208. }
  209. // setup signal handler
  210. if (!BSignal_Init(&ss, signal_handler, NULL)) {
  211. BLog(BLOG_ERROR, "BSignal_Init failed");
  212. goto fail2;
  213. }
  214. // read config file
  215. uint8_t *file;
  216. size_t file_len;
  217. if (!read_file(options.config_file, &file, &file_len)) {
  218. BLog(BLOG_ERROR, "failed to read config file");
  219. goto fail3;
  220. }
  221. // parse config file
  222. if (!NCDConfigParser_Parse((char *)file, file_len, &config_ast)) {
  223. BLog(BLOG_ERROR, "NCDConfigParser_Parse failed");
  224. free(file);
  225. goto fail3;
  226. }
  227. // fee config file memory
  228. free(file);
  229. // init module params
  230. struct NCDModuleInitParams params;
  231. params.reactor = &ss;
  232. params.manager = &manager;
  233. // init modules
  234. size_t num_inited_modules = 0;
  235. for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) {
  236. if ((*g)->func_globalinit && !(*g)->func_globalinit(params)) {
  237. BLog(BLOG_ERROR, "globalinit failed for some module");
  238. goto fail5;
  239. }
  240. num_inited_modules++;
  241. }
  242. // init processes list
  243. LinkedList2_Init(&processes);
  244. // init processes
  245. struct NCDConfig_interfaces *conf = config_ast;
  246. while (conf) {
  247. process_new(conf);
  248. conf = conf->next;
  249. }
  250. // enter event loop
  251. BLog(BLOG_NOTICE, "entering event loop");
  252. BReactor_Exec(&ss);
  253. // free processes
  254. LinkedList2Node *n;
  255. while (n = LinkedList2_GetFirst(&processes)) {
  256. struct process *p = UPPER_OBJECT(n, struct process, list_node);
  257. process_free(p);
  258. }
  259. fail5:
  260. // free modules
  261. while (num_inited_modules > 0) {
  262. const struct NCDModuleGroup **g = &ncd_modules[num_inited_modules - 1];
  263. if ((*g)->func_globalfree) {
  264. (*g)->func_globalfree();
  265. }
  266. num_inited_modules--;
  267. }
  268. // free configuration
  269. NCDConfig_free_interfaces(config_ast);
  270. fail3:
  271. // remove signal handler
  272. BSignal_Finish();
  273. fail2:
  274. // free process manager
  275. BProcessManager_Free(&manager);
  276. fail1a:
  277. // free reactor
  278. BReactor_Free(&ss);
  279. fail1:
  280. // free logger
  281. BLog(BLOG_NOTICE, "exiting");
  282. BLog_Free();
  283. fail0:
  284. // finish objects
  285. DebugObjectGlobal_Finish();
  286. return 1;
  287. }
  288. void print_help (const char *name)
  289. {
  290. printf(
  291. "Usage:\n"
  292. " %s\n"
  293. " [--help]\n"
  294. " [--version]\n"
  295. " [--logger <"LOGGERS_STRING">]\n"
  296. #ifndef BADVPN_USE_WINAPI
  297. " (logger=syslog?\n"
  298. " [--syslog-facility <string>]\n"
  299. " [--syslog-ident <string>]\n"
  300. " )\n"
  301. #endif
  302. " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n"
  303. " [--channel-loglevel <channel-name> <0-5/none/error/warning/notice/info/debug>] ...\n"
  304. " --config-file <file>\n"
  305. " [--retry-time <ms>]\n",
  306. name
  307. );
  308. }
  309. void print_version (void)
  310. {
  311. printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
  312. }
  313. int parse_arguments (int argc, char *argv[])
  314. {
  315. if (argc <= 0) {
  316. return 0;
  317. }
  318. options.help = 0;
  319. options.version = 0;
  320. options.logger = LOGGER_STDOUT;
  321. #ifndef BADVPN_USE_WINAPI
  322. options.logger_syslog_facility = "daemon";
  323. options.logger_syslog_ident = argv[0];
  324. #endif
  325. options.loglevel = -1;
  326. for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
  327. options.loglevels[i] = -1;
  328. }
  329. options.config_file = NULL;
  330. options.retry_time = DEFAULT_RETRY_TIME;
  331. for (int i = 1; i < argc; i++) {
  332. char *arg = argv[i];
  333. if (!strcmp(arg, "--help")) {
  334. options.help = 1;
  335. }
  336. else if (!strcmp(arg, "--version")) {
  337. options.version = 1;
  338. }
  339. else if (!strcmp(arg, "--logger")) {
  340. if (1 >= argc - i) {
  341. fprintf(stderr, "%s: requires an argument\n", arg);
  342. return 0;
  343. }
  344. char *arg2 = argv[i + 1];
  345. if (!strcmp(arg2, "stdout")) {
  346. options.logger = LOGGER_STDOUT;
  347. }
  348. #ifndef BADVPN_USE_WINAPI
  349. else if (!strcmp(arg2, "syslog")) {
  350. options.logger = LOGGER_SYSLOG;
  351. }
  352. #endif
  353. else {
  354. fprintf(stderr, "%s: wrong argument\n", arg);
  355. return 0;
  356. }
  357. i++;
  358. }
  359. #ifndef BADVPN_USE_WINAPI
  360. else if (!strcmp(arg, "--syslog-facility")) {
  361. if (1 >= argc - i) {
  362. fprintf(stderr, "%s: requires an argument\n", arg);
  363. return 0;
  364. }
  365. options.logger_syslog_facility = argv[i + 1];
  366. i++;
  367. }
  368. else if (!strcmp(arg, "--syslog-ident")) {
  369. if (1 >= argc - i) {
  370. fprintf(stderr, "%s: requires an argument\n", arg);
  371. return 0;
  372. }
  373. options.logger_syslog_ident = argv[i + 1];
  374. i++;
  375. }
  376. #endif
  377. else if (!strcmp(arg, "--loglevel")) {
  378. if (1 >= argc - i) {
  379. fprintf(stderr, "%s: requires an argument\n", arg);
  380. return 0;
  381. }
  382. if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) {
  383. fprintf(stderr, "%s: wrong argument\n", arg);
  384. return 0;
  385. }
  386. i++;
  387. }
  388. else if (!strcmp(arg, "--channel-loglevel")) {
  389. if (2 >= argc - i) {
  390. fprintf(stderr, "%s: requires two arguments\n", arg);
  391. return 0;
  392. }
  393. int channel = BLogGlobal_GetChannelByName(argv[i + 1]);
  394. if (channel < 0) {
  395. fprintf(stderr, "%s: wrong channel argument\n", arg);
  396. return 0;
  397. }
  398. int loglevel = parse_loglevel(argv[i + 2]);
  399. if (loglevel < 0) {
  400. fprintf(stderr, "%s: wrong loglevel argument\n", arg);
  401. return 0;
  402. }
  403. options.loglevels[channel] = loglevel;
  404. i += 2;
  405. }
  406. else if (!strcmp(arg, "--config-file")) {
  407. if (1 >= argc - i) {
  408. fprintf(stderr, "%s: requires an argument\n", arg);
  409. return 0;
  410. }
  411. options.config_file = argv[i + 1];
  412. i++;
  413. }
  414. else if (!strcmp(arg, "--retry-time")) {
  415. if (1 >= argc - i) {
  416. fprintf(stderr, "%s: requires an argument\n", arg);
  417. return 0;
  418. }
  419. if ((options.retry_time = atoi(argv[i + 1])) < 0) {
  420. fprintf(stderr, "%s: wrong argument\n", arg);
  421. return 0;
  422. }
  423. i++;
  424. }
  425. else {
  426. fprintf(stderr, "unknown option: %s\n", arg);
  427. return 0;
  428. }
  429. }
  430. if (options.help || options.version) {
  431. return 1;
  432. }
  433. if (!options.config_file) {
  434. fprintf(stderr, "--config-file is required\n");
  435. return 0;
  436. }
  437. return 1;
  438. }
  439. void signal_handler (void *unused)
  440. {
  441. BLog(BLOG_NOTICE, "termination requested");
  442. if (terminating) {
  443. return;
  444. }
  445. terminating = 1;
  446. if (LinkedList2_IsEmpty(&processes)) {
  447. BReactor_Quit(&ss, 1);
  448. return;
  449. }
  450. // schedule work for all processes
  451. LinkedList2Iterator it;
  452. LinkedList2Iterator_InitForward(&it, &processes);
  453. LinkedList2Node *n;
  454. while (n = LinkedList2Iterator_Next(&it)) {
  455. struct process *p = UPPER_OBJECT(n, struct process, list_node);
  456. process_schedule_work(p);
  457. }
  458. }
  459. const struct NCDModule * find_module (const char *name)
  460. {
  461. for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) {
  462. for (const struct NCDModule *m = (*g)->modules; m->type; m++) {
  463. if (!strcmp(m->type, name)) {
  464. return m;
  465. }
  466. }
  467. }
  468. return NULL;
  469. }
  470. int statement_init (struct statement *s, struct NCDConfig_statements *conf)
  471. {
  472. s->object_name = NULL;
  473. s->method_name = NULL;
  474. s->name = NULL;
  475. s->first_arg = NULL;
  476. // set object name
  477. if (conf->objname) {
  478. if (!(s->object_name = NCDConfig_concat_strings(conf->objname))) {
  479. BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
  480. goto fail;
  481. }
  482. }
  483. // set method name
  484. if (!(s->method_name = NCDConfig_concat_strings(conf->names))) {
  485. BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
  486. goto fail;
  487. }
  488. // init name
  489. if (conf->name) {
  490. if (!(s->name = strdup(conf->name))) {
  491. BLog(BLOG_ERROR, "strdup failed");
  492. goto fail;
  493. }
  494. }
  495. // init arguments
  496. struct argument_elem **prevptr = &s->first_arg;
  497. struct NCDConfig_arguments *arg = conf->args;
  498. while (arg) {
  499. struct argument_elem *e = malloc(sizeof(*e));
  500. if (!e) {
  501. BLog(BLOG_ERROR, "malloc failed");
  502. goto loop_fail0;
  503. }
  504. switch (arg->type) {
  505. case NCDCONFIG_ARG_STRING: {
  506. if (!NCDValue_InitString(&e->val, arg->string)) {
  507. BLog(BLOG_ERROR, "NCDValue_InitString failed");
  508. goto loop_fail1;
  509. }
  510. e->is_var = 0;
  511. } break;
  512. case NCDCONFIG_ARG_VAR: {
  513. if (!(e->var.modname = strdup(arg->var->value))) {
  514. BLog(BLOG_ERROR, "strdup failed");
  515. goto loop_fail1;
  516. }
  517. if (!(e->var.varname = (arg->var->next ? NCDConfig_concat_strings(arg->var->next) : strdup("")))) {
  518. BLog(BLOG_ERROR, "NCDConfig_concat_strings/strdup failed");
  519. free(e->var.modname);
  520. goto loop_fail1;
  521. }
  522. e->is_var = 1;
  523. } break;
  524. default:
  525. ASSERT(0);
  526. }
  527. *prevptr = e;
  528. e->next_arg = NULL;
  529. prevptr = &e->next_arg;
  530. arg = arg->next;
  531. continue;
  532. loop_fail1:
  533. free(e);
  534. loop_fail0:
  535. goto fail;
  536. }
  537. return 1;
  538. fail:
  539. statement_free_args(s);
  540. free(s->name);
  541. free(s->method_name);
  542. free(s->object_name);
  543. return 0;
  544. }
  545. void statement_free (struct statement *s)
  546. {
  547. // free arguments
  548. statement_free_args(s);
  549. // free names
  550. free(s->name);
  551. free(s->method_name);
  552. free(s->object_name);
  553. }
  554. void statement_free_args (struct statement *s)
  555. {
  556. struct argument_elem *e = s->first_arg;
  557. while (e) {
  558. if (e->is_var) {
  559. free(e->var.modname);
  560. free(e->var.varname);
  561. } else {
  562. NCDValue_Free(&e->val);
  563. }
  564. struct argument_elem *n = e->next_arg;
  565. free(e);
  566. e = n;
  567. }
  568. }
  569. int process_new (struct NCDConfig_interfaces *conf)
  570. {
  571. // allocate strucure
  572. struct process *p = malloc(sizeof(*p));
  573. if (!p) {
  574. BLog(BLOG_ERROR, "malloc failed");
  575. goto fail0;
  576. }
  577. // init name
  578. if (!(p->name = strdup(conf->name))) {
  579. BLog(BLOG_ERROR, "strdup failed");
  580. goto fail1;
  581. }
  582. // count statements
  583. size_t num_st = 0;
  584. struct NCDConfig_statements *st = conf->statements;
  585. while (st) {
  586. if (num_st == SIZE_MAX) {
  587. BLog(BLOG_ERROR, "too many statements");
  588. goto fail2;
  589. }
  590. num_st++;
  591. st = st->next;
  592. }
  593. // allocate statements array
  594. if (!(p->statements = BAllocArray(num_st, sizeof(p->statements[0])))) {
  595. goto fail2;
  596. }
  597. p->num_statements = 0;
  598. // init statements
  599. st = conf->statements;
  600. while (st) {
  601. struct process_statement *ps = &p->statements[p->num_statements];
  602. ps->p = p;
  603. ps->i = p->num_statements;
  604. if (!statement_init(&ps->s, st)) {
  605. goto fail3;
  606. }
  607. ps->state = SSTATE_FORGOTTEN;
  608. ps->have_error = 0;
  609. p->num_statements++;
  610. st = st->next;
  611. }
  612. // set AP=0
  613. p->ap = 0;
  614. // set FP=0
  615. p->fp = 0;
  616. // init timer
  617. BTimer_Init(&p->wait_timer, 0, (BTimer_handler)process_wait_timer_handler, p);
  618. // init advance job
  619. BPending_Init(&p->advance_job, BReactor_PendingGroup(&ss), (BPending_handler)process_advance_job_handler, p);
  620. // init work job
  621. BPending_Init(&p->work_job, BReactor_PendingGroup(&ss), (BPending_handler)process_work_job_handler, p);
  622. // insert to processes list
  623. LinkedList2_Append(&processes, &p->list_node);
  624. // schedule work
  625. BPending_Set(&p->work_job);
  626. return 1;
  627. fail3:
  628. process_free_statements(p);
  629. fail2:
  630. free(p->name);
  631. fail1:
  632. free(p);
  633. fail0:
  634. BLog(BLOG_ERROR, "failed to initialize process %s", conf->name);
  635. return 0;
  636. }
  637. void process_free (struct process *p)
  638. {
  639. ASSERT(p->ap == 0)
  640. ASSERT(p->fp == 0)
  641. // remove from processes list
  642. LinkedList2_Remove(&processes, &p->list_node);
  643. // free work job
  644. BPending_Free(&p->work_job);
  645. // free advance job
  646. BPending_Free(&p->advance_job);
  647. // free timer
  648. BReactor_RemoveTimer(&ss, &p->wait_timer);
  649. // free statements
  650. process_free_statements(p);
  651. // free name
  652. free(p->name);
  653. // free strucure
  654. free(p);
  655. }
  656. size_t process_rap (struct process *p)
  657. {
  658. if (p->ap > 0 && p->statements[p->ap - 1].state == SSTATE_CHILD) {
  659. return (p->ap - 1);
  660. } else {
  661. return p->ap;
  662. }
  663. }
  664. void process_free_statements (struct process *p)
  665. {
  666. // free statments
  667. while (p->num_statements > 0) {
  668. statement_free(&p->statements[p->num_statements - 1].s);
  669. p->num_statements--;
  670. }
  671. // free stataments array
  672. free(p->statements);
  673. }
  674. void process_assert_pointers (struct process *p)
  675. {
  676. ASSERT(p->ap <= p->num_statements)
  677. ASSERT(p->fp >= p->ap)
  678. ASSERT(p->fp <= p->num_statements)
  679. // check AP
  680. for (size_t i = 0; i < p->ap; i++) {
  681. if (i == p->ap - 1) {
  682. ASSERT(p->statements[i].state == SSTATE_ADULT || p->statements[i].state == SSTATE_CHILD)
  683. } else {
  684. ASSERT(p->statements[i].state == SSTATE_ADULT)
  685. }
  686. }
  687. // check FP
  688. size_t fp = p->num_statements;
  689. while (fp > 0 && p->statements[fp - 1].state == SSTATE_FORGOTTEN) {
  690. fp--;
  691. }
  692. ASSERT(p->fp == fp)
  693. }
  694. void process_log (struct process *p, int level, const char *fmt, ...)
  695. {
  696. va_list vl;
  697. va_start(vl, fmt);
  698. BLog_Append("process %s: ", p->name);
  699. BLog_LogToChannelVarArg(BLOG_CURRENT_CHANNEL, level, fmt, vl);
  700. va_end(vl);
  701. }
  702. void process_schedule_work (struct process *p)
  703. {
  704. process_assert_pointers(p);
  705. // stop timer
  706. BReactor_RemoveTimer(&ss, &p->wait_timer);
  707. // stop advance job
  708. BPending_Unset(&p->advance_job);
  709. // schedule work
  710. BPending_Set(&p->work_job);
  711. }
  712. void process_work_job_handler (struct process *p)
  713. {
  714. process_assert_pointers(p);
  715. ASSERT(!BTimer_IsRunning(&p->wait_timer))
  716. ASSERT(!BPending_IsSet(&p->advance_job))
  717. if (terminating) {
  718. if (p->fp == 0) {
  719. // finished retreating
  720. process_free(p);
  721. // if there are no more processes, exit program
  722. if (LinkedList2_IsEmpty(&processes)) {
  723. BReactor_Quit(&ss, 1);
  724. }
  725. } else {
  726. // order the last living statement to die, if needed
  727. struct process_statement *ps = &p->statements[p->fp - 1];
  728. ASSERT(ps->state != SSTATE_FORGOTTEN)
  729. if (ps->state != SSTATE_DYING) {
  730. process_statement_log(ps, BLOG_INFO, "killing");
  731. // order it to die
  732. NCDModuleInst_Event(&ps->inst, NCDMODULE_TOEVENT_DIE);
  733. // set statement state DYING
  734. ps->state = SSTATE_DYING;
  735. // update AP
  736. if (p->ap > ps->i) {
  737. p->ap = ps->i;
  738. }
  739. }
  740. process_assert_pointers(p);
  741. }
  742. return;
  743. }
  744. if (p->ap == p->fp) {
  745. if (p->ap == process_rap(p)) {
  746. if (p->ap == p->num_statements) {
  747. // all statements are up
  748. process_log(p, BLOG_INFO, "victory");
  749. } else {
  750. struct process_statement *ps = &p->statements[p->ap];
  751. ASSERT(ps->state == SSTATE_FORGOTTEN)
  752. // clear expired error
  753. if (ps->have_error && ps->error_until <= btime_gettime()) {
  754. ps->have_error = 0;
  755. }
  756. if (ps->have_error) {
  757. // next statement has error, wait
  758. process_statement_log(ps, BLOG_INFO, "waiting after error");
  759. BReactor_SetTimerAbsolute(&ss, &p->wait_timer, ps->error_until);
  760. } else {
  761. // schedule advance
  762. BPending_Set(&p->advance_job);
  763. }
  764. }
  765. } else {
  766. ASSERT(p->ap > 0)
  767. ASSERT(p->ap <= p->num_statements)
  768. struct process_statement *ps = &p->statements[p->ap - 1];
  769. ASSERT(ps->state == SSTATE_CHILD)
  770. process_statement_log(ps, BLOG_INFO, "clean");
  771. // report clean
  772. NCDModuleInst_Event(&ps->inst, NCDMODULE_TOEVENT_CLEAN);
  773. }
  774. } else {
  775. // order the last living statement to die, if needed
  776. struct process_statement *ps = &p->statements[p->fp - 1];
  777. if (ps->state != SSTATE_DYING) {
  778. process_statement_log(ps, BLOG_INFO, "killing");
  779. // order it to die
  780. NCDModuleInst_Event(&ps->inst, NCDMODULE_TOEVENT_DIE);
  781. // set statement state DYING
  782. ps->state = SSTATE_DYING;
  783. }
  784. }
  785. process_assert_pointers(p);
  786. }
  787. void process_advance_job_handler (struct process *p)
  788. {
  789. process_assert_pointers(p);
  790. ASSERT(p->ap == p->fp)
  791. ASSERT(p->ap == process_rap(p))
  792. ASSERT(p->ap < p->num_statements)
  793. ASSERT(!p->statements[p->ap].have_error)
  794. ASSERT(!BPending_IsSet(&p->work_job))
  795. ASSERT(!BTimer_IsRunning(&p->wait_timer))
  796. ASSERT(!terminating)
  797. struct process_statement *ps = &p->statements[p->ap];
  798. ASSERT(ps->state == SSTATE_FORGOTTEN)
  799. process_statement_log(ps, BLOG_INFO, "initializing");
  800. NCDModuleInst *method_object = NULL;
  801. // construct type
  802. if (!ps->s.object_name) {
  803. // this is a function_call(); type is "function_call"
  804. if (!(ps->type = strdup(ps->s.method_name))) {
  805. process_statement_log(ps, BLOG_ERROR, "strdup failed");
  806. goto fail0;
  807. }
  808. } else {
  809. // this is a some.object.somewhere->method_call(); type is "typeof(some.object.somewhere)::method_call"
  810. // resolve object
  811. struct process_statement *obj_ps = process_resolve_object(p, p->ap, ps->s.object_name);
  812. if (!obj_ps) {
  813. process_statement_log(ps, BLOG_ERROR, "failed to resolve object %s for method call", ps->s.object_name);
  814. goto fail0;
  815. }
  816. ASSERT(obj_ps->state == SSTATE_ADULT)
  817. // build type string
  818. if (!(ps->type = concat_strings(3, obj_ps->type, "::", ps->s.method_name))) {
  819. process_statement_log(ps, BLOG_ERROR, "concat_strings failed");
  820. goto fail0;
  821. }
  822. method_object = &obj_ps->inst;
  823. }
  824. // find module to instantiate
  825. const struct NCDModule *module = find_module(ps->type);
  826. if (!module) {
  827. process_statement_log(ps, BLOG_ERROR, "failed to find module: %s", ps->type);
  828. goto fail1;
  829. }
  830. // init arguments list
  831. NCDValue_InitList(&ps->inst_args);
  832. // build arguments
  833. struct argument_elem *arg = ps->s.first_arg;
  834. while (arg) {
  835. // resolve argument
  836. NCDValue v;
  837. if (arg->is_var) {
  838. if (!process_resolve_variable(p, p->ap, arg->var.modname, arg->var.varname, &v)) {
  839. process_statement_log(ps, BLOG_ERROR, "failed to resolve variable");
  840. goto fail2;
  841. }
  842. } else {
  843. if (!NCDValue_InitCopy(&v, &arg->val)) {
  844. process_statement_log(ps, BLOG_ERROR, "NCDValue_InitCopy failed");
  845. goto fail2;
  846. }
  847. }
  848. // move to list
  849. if (!NCDValue_ListAppend(&ps->inst_args, v)) {
  850. process_statement_log(ps, BLOG_ERROR, "NCDValue_ListAppend failed");
  851. NCDValue_Free(&v);
  852. goto fail2;
  853. }
  854. arg = arg->next_arg;
  855. }
  856. // generate log prefix
  857. snprintf(ps->logprefix, sizeof(ps->logprefix), "process %s: statement %zu: module: ", p->name, ps->i);
  858. // initialize module instance
  859. NCDModuleInst_Init(
  860. &ps->inst, module, method_object, &ps->inst_args, ps->logprefix, &ss, &manager,
  861. (NCDModule_handler_event)process_statement_instance_handler_event,
  862. (NCDModule_handler_getvar)process_statement_instance_handler_getvar,
  863. (NCDModule_handler_getobj)process_statement_instance_handler_getobj,
  864. ps
  865. );
  866. // set statement state CHILD
  867. ps->state = SSTATE_CHILD;
  868. // increment AP
  869. p->ap++;
  870. // increment FP
  871. p->fp++;
  872. process_assert_pointers(p);
  873. return;
  874. fail2:
  875. NCDValue_Free(&ps->inst_args);
  876. fail1:
  877. free(ps->type);
  878. fail0:
  879. // mark error
  880. process_statement_set_error(ps);
  881. // schedule work to start the timer
  882. BPending_Set(&p->work_job);
  883. }
  884. void process_wait_timer_handler (struct process *p)
  885. {
  886. process_assert_pointers(p);
  887. ASSERT(p->ap == p->fp)
  888. ASSERT(p->ap == process_rap(p))
  889. ASSERT(p->ap < p->num_statements)
  890. ASSERT(p->statements[p->ap].have_error)
  891. ASSERT(!BPending_IsSet(&p->work_job))
  892. ASSERT(!BPending_IsSet(&p->advance_job))
  893. ASSERT(!terminating)
  894. process_log(p, BLOG_INFO, "retrying");
  895. // clear error
  896. p->statements[p->ap].have_error = 0;
  897. // schedule work
  898. BPending_Set(&p->work_job);
  899. }
  900. struct process_statement * process_find_statement (struct process *p, size_t pos, const char *name)
  901. {
  902. process_assert_pointers(p);
  903. ASSERT(pos >= 0)
  904. ASSERT(pos <= process_rap(p))
  905. for (size_t i = pos; i > 0; i--) {
  906. struct process_statement *ps = &p->statements[i - 1];
  907. if (ps->s.name && !strcmp(ps->s.name, name)) {
  908. return ps;
  909. }
  910. }
  911. return NULL;
  912. }
  913. int process_resolve_variable (struct process *p, size_t pos, const char *modname, const char *varname, NCDValue *out)
  914. {
  915. process_assert_pointers(p);
  916. ASSERT(pos >= 0)
  917. ASSERT(pos <= process_rap(p))
  918. ASSERT(modname)
  919. ASSERT(varname)
  920. // find referred-to statement
  921. struct process_statement *rps = process_find_statement(p, pos, modname);
  922. if (!rps) {
  923. process_log(p, BLOG_ERROR, "unknown statement name in variable: %s.%s", modname, varname);
  924. return 0;
  925. }
  926. ASSERT(rps->state == SSTATE_ADULT)
  927. // resolve variable
  928. if (!NCDModuleInst_GetVar(&rps->inst, varname, out)) {
  929. process_log(p, BLOG_ERROR, "failed to resolve variable: %s.%s", modname, varname);
  930. return 0;
  931. }
  932. return 1;
  933. }
  934. struct process_statement * process_resolve_object (struct process *p, size_t pos, const char *objname)
  935. {
  936. process_assert_pointers(p);
  937. ASSERT(pos >= 0)
  938. ASSERT(pos <= process_rap(p))
  939. ASSERT(objname)
  940. struct process_statement *res_ps = NULL;
  941. // copy name
  942. char *name = strdup(objname);
  943. if (!name) {
  944. process_log(p, BLOG_ERROR, "strdup failed");
  945. goto fail0;
  946. }
  947. // split name into first name and the rest
  948. const char *objname_first = name;
  949. const char *objname_rest = NULL;
  950. char *dot = strstr(name, ".");
  951. if (dot) {
  952. *dot = '\0';
  953. objname_rest = dot + 1;
  954. }
  955. // find referred-to statement of the first name
  956. struct process_statement *first_ps = process_find_statement(p, pos, objname_first);
  957. if (!first_ps) {
  958. process_log(p, BLOG_ERROR, "failed to find first object for %s", objname);
  959. goto fail1;
  960. }
  961. ASSERT(first_ps->state == SSTATE_ADULT)
  962. if (!objname_rest) {
  963. // this is the target
  964. res_ps = first_ps;
  965. } else {
  966. // query this statement
  967. NCDModuleInst *inst = NCDModuleInst_GetObj(&first_ps->inst, objname_rest);
  968. if (!inst) {
  969. process_log(p, BLOG_ERROR, "object failed to provide contained object for %s", objname);
  970. goto fail1;
  971. }
  972. res_ps = UPPER_OBJECT(inst, struct process_statement, inst);
  973. ASSERT(res_ps->state == SSTATE_ADULT)
  974. }
  975. fail1:
  976. free(name);
  977. fail0:
  978. return res_ps;
  979. }
  980. void process_statement_log (struct process_statement *ps, int level, const char *fmt, ...)
  981. {
  982. va_list vl;
  983. va_start(vl, fmt);
  984. BLog_Append("process %s: statement %zu: ", ps->p->name, ps->i);
  985. BLog_LogToChannelVarArg(BLOG_CURRENT_CHANNEL, level, fmt, vl);
  986. va_end(vl);
  987. }
  988. void process_statement_set_error (struct process_statement *ps)
  989. {
  990. ASSERT(ps->state == SSTATE_FORGOTTEN)
  991. ps->have_error = 1;
  992. ps->error_until = btime_add(btime_gettime(), options.retry_time);
  993. }
  994. void process_statement_instance_handler_event (struct process_statement *ps, int event)
  995. {
  996. ASSERT(ps->state == SSTATE_CHILD || ps->state == SSTATE_ADULT || ps->state == SSTATE_DYING)
  997. struct process *p = ps->p;
  998. process_assert_pointers(p);
  999. // schedule work
  1000. process_schedule_work(p);
  1001. switch (event) {
  1002. case NCDMODULE_EVENT_UP: {
  1003. ASSERT(ps->state == SSTATE_CHILD)
  1004. process_statement_log(ps, BLOG_INFO, "up");
  1005. // set state ADULT
  1006. ps->state = SSTATE_ADULT;
  1007. } break;
  1008. case NCDMODULE_EVENT_DOWN: {
  1009. ASSERT(ps->state == SSTATE_ADULT)
  1010. process_statement_log(ps, BLOG_INFO, "down");
  1011. // set state CHILD
  1012. ps->state = SSTATE_CHILD;
  1013. // update AP
  1014. if (p->ap > ps->i + 1) {
  1015. p->ap = ps->i + 1;
  1016. }
  1017. } break;
  1018. case NCDMODULE_EVENT_DEAD: {
  1019. int is_error = NCDModuleInst_HaveError(&ps->inst);
  1020. if (is_error) {
  1021. process_statement_log(ps, BLOG_ERROR, "died with error");
  1022. } else {
  1023. process_statement_log(ps, BLOG_INFO, "died");
  1024. }
  1025. // free instance
  1026. NCDModuleInst_Free(&ps->inst);
  1027. // free instance arguments
  1028. NCDValue_Free(&ps->inst_args);
  1029. // free type
  1030. free(ps->type);
  1031. // set state FORGOTTEN
  1032. ps->state = SSTATE_FORGOTTEN;
  1033. // set error
  1034. if (is_error) {
  1035. process_statement_set_error(ps);
  1036. }
  1037. // update AP
  1038. if (p->ap > ps->i) {
  1039. p->ap = ps->i;
  1040. }
  1041. // update FP
  1042. while (p->fp > 0 && p->statements[p->fp - 1].state == SSTATE_FORGOTTEN) {
  1043. p->fp--;
  1044. }
  1045. } break;
  1046. }
  1047. }
  1048. int process_statement_instance_handler_getvar (struct process_statement *ps, const char *modname, const char *varname, NCDValue *out)
  1049. {
  1050. ASSERT(ps->state != SSTATE_FORGOTTEN)
  1051. if (ps->i > process_rap(ps->p)) {
  1052. process_statement_log(ps, BLOG_ERROR, "tried to resolve variable %s.%s but it's dirty", modname, varname);
  1053. return 0;
  1054. }
  1055. return process_resolve_variable(ps->p, ps->i, modname, varname, out);
  1056. }
  1057. NCDModuleInst * process_statement_instance_handler_getobj (struct process_statement *ps, const char *objname)
  1058. {
  1059. ASSERT(ps->state != SSTATE_FORGOTTEN)
  1060. if (ps->i > process_rap(ps->p)) {
  1061. process_statement_log(ps, BLOG_ERROR, "tried to resolve object %s but it's dirty", objname);
  1062. return 0;
  1063. }
  1064. struct process_statement *rps = process_resolve_object(ps->p, ps->i, objname);
  1065. if (!rps) {
  1066. return NULL;
  1067. }
  1068. return &rps->inst;
  1069. }