Просмотр исходного кода

ncd: add support for process templates. Add module synchronous_process.

ambrop7 15 лет назад
Родитель
Сommit
8c49be9dc8

+ 1 - 0
blog_channels.txt

@@ -18,6 +18,7 @@ ncd_print 4
 ncd_blocker 4
 ncd_ip_in_network 4
 ncd_run 4
+ncd_synchronous_process 4
 ncd_net_backend_waitdevice 4
 ncd_net_backend_waitlink 4
 ncd_net_backend_badvpn 4

+ 195 - 174
generated/NCDConfigParser_parse.c

@@ -70,17 +70,18 @@ struct parser_out {
 **                       defined, then do no error processing.
 */
 #define YYCODETYPE unsigned char
-#define YYNOCODE 21
+#define YYNOCODE 23
 #define YYACTIONTYPE unsigned char
 #define ParseTOKENTYPE void *
 typedef union {
   int yyinit;
   ParseTOKENTYPE yy0;
-  struct NCDConfig_strings * yy4;
-  struct NCDConfig_interfaces * yy14;
-  struct NCDConfig_arguments * yy24;
-  struct NCDConfig_statements * yy30;
-  char * yy33;
+  struct NCDConfig_arguments * yy8;
+  char * yy9;
+  struct NCDConfig_interfaces * yy18;
+  int yy20;
+  struct NCDConfig_statements * yy38;
+  struct NCDConfig_strings * yy40;
 } YYMINORTYPE;
 #ifndef YYSTACKDEPTH
 #define YYSTACKDEPTH 0
@@ -89,8 +90,8 @@ typedef union {
 #define ParseARG_PDECL ,struct parser_out *parser_out
 #define ParseARG_FETCH struct parser_out *parser_out = yypParser->parser_out
 #define ParseARG_STORE yypParser->parser_out = parser_out
-#define YYNSTATE 34
-#define YYNRULE 17
+#define YYNSTATE 36
+#define YYNRULE 19
 #define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
 #define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
 #define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
@@ -160,37 +161,37 @@ static const YYMINORTYPE yyzerominor = { 0 };
 **  yy_default[]       Default action for each state.
 */
 static const YYACTIONTYPE yy_action[] = {
- /*     0 */    22,   18,   30,   22,   24,   30,   14,   20,   15,   22,
- /*    10 */    26,   32,   52,   29,   22,   21,   31,   17,   13,   20,
- /*    20 */    27,   13,   33,   13,    1,   34,   16,   11,   19,   28,
- /*    30 */    23,   25,    5,    9,    8,    6,   53,    3,    2,   53,
- /*    40 */    10,   12,   53,    4,   53,    7,
+ /*     0 */    14,   22,   18,   32,   27,   28,   15,   56,   22,   24,
+ /*    10 */    32,   20,   26,   22,   20,   34,   17,   13,   15,   21,
+ /*    20 */    22,   31,   33,   29,   13,    1,   35,   13,   11,   36,
+ /*    30 */    30,   19,   23,   16,   25,    6,    9,    3,   10,   57,
+ /*    40 */    57,    7,    2,   57,   12,   57,    4,    5,    8,
 };
 static const YYCODETYPE yy_lookahead[] = {
- /*     0 */    15,   16,   17,   15,   16,   17,   13,    2,    1,   15,
- /*    10 */    13,   17,   19,    2,   15,   10,   17,   14,   15,    2,
- /*    20 */    14,   15,   14,   15,    5,    0,    2,    8,   18,   15,
- /*    30 */    15,   18,    3,    6,    4,    7,   20,   11,    5,   20,
- /*    40 */     9,    6,   20,   11,   20,    7,
+ /*     0 */    14,   16,   17,   18,   11,   12,   20,   21,   16,   17,
+ /*    10 */    18,    1,   14,   16,    1,   18,   15,   16,   20,    9,
+ /*    20 */    16,    1,   18,   15,   16,    4,   15,   16,    7,    0,
+ /*    30 */    16,   19,   16,    1,   19,    2,    5,    3,    8,   22,
+ /*    40 */    22,    6,    4,   22,    5,   22,   10,   10,    6,
 };
-#define YY_SHIFT_USE_DFLT (-1)
+#define YY_SHIFT_USE_DFLT (-8)
 #define YY_SHIFT_MAX 25
 static const signed char yy_shift_ofst[] = {
- /*     0 */     7,    5,    5,    5,    5,   17,   17,   17,    7,   11,
- /*    10 */    17,   17,   11,   19,   25,   24,   29,   30,   27,   28,
- /*    20 */    31,   26,   32,   33,   35,   38,
+ /*     0 */    -7,   10,   10,   -7,   10,   10,   13,   13,   13,   20,
+ /*    10 */    13,   13,   20,   21,   29,   32,   33,   34,   31,   35,
+ /*    20 */    30,   36,   37,   38,   39,   42,
 };
 #define YY_REDUCE_USE_DFLT (-16)
 #define YY_REDUCE_MAX 12
 static const signed char yy_reduce_ofst[] = {
- /*     0 */    -7,  -15,  -12,   -6,   -1,    3,    6,    8,   -3,   10,
- /*    10 */    14,   15,   13,
+ /*     0 */   -14,  -15,   -8,   -2,   -3,    4,    1,    8,   11,   12,
+ /*    10 */    14,   16,   15,
 };
 static const YYACTIONTYPE yy_default[] = {
- /*     0 */    51,   43,   43,   51,   51,   51,   37,   39,   35,   49,
- /*    10 */    51,   51,   49,   51,   51,   51,   51,   51,   51,   51,
- /*    20 */    41,   45,   46,   51,   51,   51,   36,   38,   42,   50,
- /*    30 */    44,   48,   47,   40,
+ /*     0 */    55,   45,   45,   37,   55,   55,   55,   39,   41,   51,
+ /*    10 */    55,   55,   51,   55,   55,   55,   55,   55,   55,   55,
+ /*    20 */    43,   47,   48,   55,   55,   55,   38,   53,   54,   40,
+ /*    30 */    44,   52,   46,   50,   49,   42,
 };
 #define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
 
@@ -284,11 +285,12 @@ void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
 /* For tracing shifts, the names of all terminals and nonterminals
 ** are required.  The following table supplies these names */
 static const char *const yyTokenName[] = { 
-  "$",             "PROCESS",       "NAME",          "CURLY_OPEN",  
-  "CURLY_CLOSE",   "ROUND_OPEN",    "ROUND_CLOSE",   "SEMICOLON",   
-  "ARROW",         "DOT",           "STRING",        "COMMA",       
-  "error",         "interfaces",    "statements",    "statement_names",
-  "statement_args_maybe",  "statement_args",  "name_maybe",    "input",       
+  "$",             "NAME",          "CURLY_OPEN",    "CURLY_CLOSE", 
+  "ROUND_OPEN",    "ROUND_CLOSE",   "SEMICOLON",     "ARROW",       
+  "DOT",           "STRING",        "COMMA",         "PROCESS",     
+  "TEMPLATE",      "error",         "interfaces",    "statements",  
+  "statement_names",  "statement_args_maybe",  "statement_args",  "name_maybe",  
+  "process_or_template",  "input",       
 };
 #endif /* NDEBUG */
 
@@ -297,8 +299,8 @@ static const char *const yyTokenName[] = {
 */
 static const char *const yyRuleName[] = {
  /*   0 */ "input ::= interfaces",
- /*   1 */ "interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE",
- /*   2 */ "interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE interfaces",
+ /*   1 */ "interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE",
+ /*   2 */ "interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE interfaces",
  /*   3 */ "statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON",
  /*   4 */ "statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements",
  /*   5 */ "statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON",
@@ -313,6 +315,8 @@ static const char *const yyRuleName[] = {
  /*  14 */ "statement_args ::= statement_names COMMA statement_args",
  /*  15 */ "name_maybe ::=",
  /*  16 */ "name_maybe ::= NAME",
+ /*  17 */ "process_or_template ::= PROCESS",
+ /*  18 */ "process_or_template ::= TEMPLATE",
 };
 #endif /* NDEBUG */
 
@@ -392,57 +396,58 @@ static void yy_destructor(
     ** inside the C code.
     */
       /* TERMINAL Destructor */
-    case 1: /* PROCESS */
-    case 2: /* NAME */
-    case 3: /* CURLY_OPEN */
-    case 4: /* CURLY_CLOSE */
-    case 5: /* ROUND_OPEN */
-    case 6: /* ROUND_CLOSE */
-    case 7: /* SEMICOLON */
-    case 8: /* ARROW */
-    case 9: /* DOT */
-    case 10: /* STRING */
-    case 11: /* COMMA */
+    case 1: /* NAME */
+    case 2: /* CURLY_OPEN */
+    case 3: /* CURLY_CLOSE */
+    case 4: /* ROUND_OPEN */
+    case 5: /* ROUND_CLOSE */
+    case 6: /* SEMICOLON */
+    case 7: /* ARROW */
+    case 8: /* DOT */
+    case 9: /* STRING */
+    case 10: /* COMMA */
+    case 11: /* PROCESS */
+    case 12: /* TEMPLATE */
 {
 #line 43 "NCDConfigParser_parse.y"
  free((yypminor->yy0)); 
-#line 410 "NCDConfigParser_parse.c"
+#line 415 "NCDConfigParser_parse.c"
 }
       break;
-    case 13: /* interfaces */
-{
-#line 52 "NCDConfigParser_parse.y"
- NCDConfig_free_interfaces((yypminor->yy14)); 
-#line 417 "NCDConfigParser_parse.c"
-}
-      break;
-    case 14: /* statements */
+    case 14: /* interfaces */
 {
 #line 53 "NCDConfigParser_parse.y"
- NCDConfig_free_statements((yypminor->yy30)); 
-#line 424 "NCDConfigParser_parse.c"
+ NCDConfig_free_interfaces((yypminor->yy18)); 
+#line 422 "NCDConfigParser_parse.c"
 }
       break;
-    case 15: /* statement_names */
+    case 15: /* statements */
 {
 #line 54 "NCDConfigParser_parse.y"
- NCDConfig_free_strings((yypminor->yy4)); 
-#line 431 "NCDConfigParser_parse.c"
+ NCDConfig_free_statements((yypminor->yy38)); 
+#line 429 "NCDConfigParser_parse.c"
 }
       break;
-    case 16: /* statement_args_maybe */
-    case 17: /* statement_args */
+    case 16: /* statement_names */
 {
 #line 55 "NCDConfigParser_parse.y"
- NCDConfig_free_arguments((yypminor->yy24)); 
-#line 439 "NCDConfigParser_parse.c"
+ NCDConfig_free_strings((yypminor->yy40)); 
+#line 436 "NCDConfigParser_parse.c"
+}
+      break;
+    case 17: /* statement_args_maybe */
+    case 18: /* statement_args */
+{
+#line 56 "NCDConfigParser_parse.y"
+ NCDConfig_free_arguments((yypminor->yy8)); 
+#line 444 "NCDConfigParser_parse.c"
 }
       break;
-    case 18: /* name_maybe */
+    case 19: /* name_maybe */
 {
-#line 57 "NCDConfigParser_parse.y"
- free((yypminor->yy33)); 
-#line 446 "NCDConfigParser_parse.c"
+#line 58 "NCDConfigParser_parse.y"
+ free((yypminor->yy9)); 
+#line 451 "NCDConfigParser_parse.c"
 }
       break;
     default:  break;   /* If no destructor action specified: do nothing */
@@ -615,12 +620,12 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
    while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
    /* Here code is inserted which will execute if the parser
    ** stack every overflows */
-#line 66 "NCDConfigParser_parse.y"
+#line 67 "NCDConfigParser_parse.y"
 
     if (yypMinor) {
         free(yypMinor->yy0);
     }
-#line 624 "NCDConfigParser_parse.c"
+#line 629 "NCDConfigParser_parse.c"
    ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
 }
 
@@ -677,23 +682,25 @@ static const struct {
   YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
   unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
 } yyRuleInfo[] = {
-  { 19, 1 },
-  { 13, 5 },
-  { 13, 6 },
+  { 21, 1 },
+  { 14, 5 },
   { 14, 6 },
-  { 14, 7 },
-  { 14, 8 },
-  { 14, 9 },
-  { 15, 1 },
-  { 15, 3 },
-  { 16, 0 },
+  { 15, 6 },
+  { 15, 7 },
+  { 15, 8 },
+  { 15, 9 },
   { 16, 1 },
+  { 16, 3 },
+  { 17, 0 },
   { 17, 1 },
-  { 17, 1 },
-  { 17, 3 },
-  { 17, 3 },
-  { 18, 0 },
   { 18, 1 },
+  { 18, 1 },
+  { 18, 3 },
+  { 18, 3 },
+  { 19, 0 },
+  { 19, 1 },
+  { 20, 1 },
+  { 20, 1 },
 };
 
 static void yy_accept(yyParser*);  /* Forward Declaration */
@@ -749,186 +756,200 @@ static void yy_reduce(
   **     break;
   */
       case 0: /* input ::= interfaces */
-#line 72 "NCDConfigParser_parse.y"
+#line 73 "NCDConfigParser_parse.y"
 {
-    parser_out->ast = yymsp[0].minor.yy14;
+    parser_out->ast = yymsp[0].minor.yy18;
 
-    if (!yymsp[0].minor.yy14) {
+    if (!yymsp[0].minor.yy18) {
         parser_out->out_of_memory = 1;
     }
 }
-#line 761 "NCDConfigParser_parse.c"
+#line 768 "NCDConfigParser_parse.c"
         break;
-      case 1: /* interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE */
-#line 80 "NCDConfigParser_parse.y"
+      case 1: /* interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE */
+#line 81 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy14 = NCDConfig_make_interfaces(yymsp[-3].minor.yy0, yymsp[-1].minor.yy30, 0, NULL);
-    if (!yygotominor.yy14) {
+    yygotominor.yy18 = NCDConfig_make_interfaces(yymsp[-4].minor.yy20, yymsp[-3].minor.yy0, yymsp[-1].minor.yy38, 0, NULL);
+    if (!yygotominor.yy18) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,1,&yymsp[-4].minor);
-  yy_destructor(yypParser,3,&yymsp[-2].minor);
-  yy_destructor(yypParser,4,&yymsp[0].minor);
+  yy_destructor(yypParser,2,&yymsp[-2].minor);
+  yy_destructor(yypParser,3,&yymsp[0].minor);
 }
-#line 774 "NCDConfigParser_parse.c"
+#line 780 "NCDConfigParser_parse.c"
         break;
-      case 2: /* interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE interfaces */
-#line 87 "NCDConfigParser_parse.y"
+      case 2: /* interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE interfaces */
+#line 88 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy14 = NCDConfig_make_interfaces(yymsp[-4].minor.yy0, yymsp[-2].minor.yy30, 1, yymsp[0].minor.yy14);
-    if (!yygotominor.yy14) {
+    yygotominor.yy18 = NCDConfig_make_interfaces(yymsp[-5].minor.yy20, yymsp[-4].minor.yy0, yymsp[-2].minor.yy38, 1, yymsp[0].minor.yy18);
+    if (!yygotominor.yy18) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,1,&yymsp[-5].minor);
-  yy_destructor(yypParser,3,&yymsp[-3].minor);
-  yy_destructor(yypParser,4,&yymsp[-1].minor);
+  yy_destructor(yypParser,2,&yymsp[-3].minor);
+  yy_destructor(yypParser,3,&yymsp[-1].minor);
 }
-#line 787 "NCDConfigParser_parse.c"
+#line 792 "NCDConfigParser_parse.c"
         break;
       case 3: /* statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */
-#line 94 "NCDConfigParser_parse.y"
+#line 95 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_statements(NULL, yymsp[-5].minor.yy4, yymsp[-3].minor.yy24, yymsp[-1].minor.yy33, NULL);
-    if (!yygotominor.yy30) {
+    yygotominor.yy38 = NCDConfig_make_statements(NULL, yymsp[-5].minor.yy40, yymsp[-3].minor.yy8, yymsp[-1].minor.yy9, NULL);
+    if (!yygotominor.yy38) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,5,&yymsp[-4].minor);
-  yy_destructor(yypParser,6,&yymsp[-2].minor);
-  yy_destructor(yypParser,7,&yymsp[0].minor);
+  yy_destructor(yypParser,4,&yymsp[-4].minor);
+  yy_destructor(yypParser,5,&yymsp[-2].minor);
+  yy_destructor(yypParser,6,&yymsp[0].minor);
 }
-#line 800 "NCDConfigParser_parse.c"
+#line 805 "NCDConfigParser_parse.c"
         break;
       case 4: /* statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements */
-#line 101 "NCDConfigParser_parse.y"
+#line 102 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_statements(NULL, yymsp[-6].minor.yy4, yymsp[-4].minor.yy24, yymsp[-2].minor.yy33, yymsp[0].minor.yy30);
-    if (!yygotominor.yy30) {
+    yygotominor.yy38 = NCDConfig_make_statements(NULL, yymsp[-6].minor.yy40, yymsp[-4].minor.yy8, yymsp[-2].minor.yy9, yymsp[0].minor.yy38);
+    if (!yygotominor.yy38) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,5,&yymsp[-5].minor);
-  yy_destructor(yypParser,6,&yymsp[-3].minor);
-  yy_destructor(yypParser,7,&yymsp[-1].minor);
+  yy_destructor(yypParser,4,&yymsp[-5].minor);
+  yy_destructor(yypParser,5,&yymsp[-3].minor);
+  yy_destructor(yypParser,6,&yymsp[-1].minor);
 }
-#line 813 "NCDConfigParser_parse.c"
+#line 818 "NCDConfigParser_parse.c"
         break;
       case 5: /* statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */
-#line 108 "NCDConfigParser_parse.y"
+#line 109 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_statements(yymsp[-7].minor.yy4, yymsp[-5].minor.yy4, yymsp[-3].minor.yy24, yymsp[-1].minor.yy33, NULL);
-    if (!yygotominor.yy30) {
+    yygotominor.yy38 = NCDConfig_make_statements(yymsp[-7].minor.yy40, yymsp[-5].minor.yy40, yymsp[-3].minor.yy8, yymsp[-1].minor.yy9, NULL);
+    if (!yygotominor.yy38) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,8,&yymsp[-6].minor);
-  yy_destructor(yypParser,5,&yymsp[-4].minor);
-  yy_destructor(yypParser,6,&yymsp[-2].minor);
-  yy_destructor(yypParser,7,&yymsp[0].minor);
+  yy_destructor(yypParser,7,&yymsp[-6].minor);
+  yy_destructor(yypParser,4,&yymsp[-4].minor);
+  yy_destructor(yypParser,5,&yymsp[-2].minor);
+  yy_destructor(yypParser,6,&yymsp[0].minor);
 }
-#line 827 "NCDConfigParser_parse.c"
+#line 832 "NCDConfigParser_parse.c"
         break;
       case 6: /* statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements */
-#line 115 "NCDConfigParser_parse.y"
+#line 116 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_statements(yymsp[-8].minor.yy4, yymsp[-6].minor.yy4, yymsp[-4].minor.yy24, yymsp[-2].minor.yy33, yymsp[0].minor.yy30);
-    if (!yygotominor.yy30) {
+    yygotominor.yy38 = NCDConfig_make_statements(yymsp[-8].minor.yy40, yymsp[-6].minor.yy40, yymsp[-4].minor.yy8, yymsp[-2].minor.yy9, yymsp[0].minor.yy38);
+    if (!yygotominor.yy38) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,8,&yymsp[-7].minor);
-  yy_destructor(yypParser,5,&yymsp[-5].minor);
-  yy_destructor(yypParser,6,&yymsp[-3].minor);
-  yy_destructor(yypParser,7,&yymsp[-1].minor);
+  yy_destructor(yypParser,7,&yymsp[-7].minor);
+  yy_destructor(yypParser,4,&yymsp[-5].minor);
+  yy_destructor(yypParser,5,&yymsp[-3].minor);
+  yy_destructor(yypParser,6,&yymsp[-1].minor);
 }
-#line 841 "NCDConfigParser_parse.c"
+#line 846 "NCDConfigParser_parse.c"
         break;
       case 7: /* statement_names ::= NAME */
-#line 122 "NCDConfigParser_parse.y"
+#line 123 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy4 = NCDConfig_make_strings(yymsp[0].minor.yy0, 0, NULL);
-    if (!yygotominor.yy4) {
+    yygotominor.yy40 = NCDConfig_make_strings(yymsp[0].minor.yy0, 0, NULL);
+    if (!yygotominor.yy40) {
         parser_out->out_of_memory = 1;
     }
 }
-#line 851 "NCDConfigParser_parse.c"
+#line 856 "NCDConfigParser_parse.c"
         break;
       case 8: /* statement_names ::= NAME DOT statement_names */
-#line 129 "NCDConfigParser_parse.y"
+#line 130 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy4 = NCDConfig_make_strings(yymsp[-2].minor.yy0, 1, yymsp[0].minor.yy4);
-    if (!yygotominor.yy4) {
+    yygotominor.yy40 = NCDConfig_make_strings(yymsp[-2].minor.yy0, 1, yymsp[0].minor.yy40);
+    if (!yygotominor.yy40) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,9,&yymsp[-1].minor);
+  yy_destructor(yypParser,8,&yymsp[-1].minor);
 }
-#line 862 "NCDConfigParser_parse.c"
+#line 867 "NCDConfigParser_parse.c"
         break;
       case 9: /* statement_args_maybe ::= */
-#line 136 "NCDConfigParser_parse.y"
+#line 137 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy24 = NULL;
+    yygotominor.yy8 = NULL;
 }
-#line 869 "NCDConfigParser_parse.c"
+#line 874 "NCDConfigParser_parse.c"
         break;
       case 10: /* statement_args_maybe ::= statement_args */
-#line 140 "NCDConfigParser_parse.y"
+#line 141 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy24 = yymsp[0].minor.yy24;
+    yygotominor.yy8 = yymsp[0].minor.yy8;
 }
-#line 876 "NCDConfigParser_parse.c"
+#line 881 "NCDConfigParser_parse.c"
         break;
       case 11: /* statement_args ::= STRING */
-#line 144 "NCDConfigParser_parse.y"
+#line 145 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy24 = NCDConfig_make_arguments_string(yymsp[0].minor.yy0, NULL);
-    if (!yygotominor.yy24) {
+    yygotominor.yy8 = NCDConfig_make_arguments_string(yymsp[0].minor.yy0, NULL);
+    if (!yygotominor.yy8) {
         parser_out->out_of_memory = 1;
     }
 }
-#line 886 "NCDConfigParser_parse.c"
+#line 891 "NCDConfigParser_parse.c"
         break;
       case 12: /* statement_args ::= statement_names */
-#line 151 "NCDConfigParser_parse.y"
+#line 152 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy24 = NCDConfig_make_arguments_var(yymsp[0].minor.yy4, NULL);
-    if (!yygotominor.yy24) {
+    yygotominor.yy8 = NCDConfig_make_arguments_var(yymsp[0].minor.yy40, NULL);
+    if (!yygotominor.yy8) {
         parser_out->out_of_memory = 1;
     }
 }
-#line 896 "NCDConfigParser_parse.c"
+#line 901 "NCDConfigParser_parse.c"
         break;
       case 13: /* statement_args ::= STRING COMMA statement_args */
-#line 158 "NCDConfigParser_parse.y"
+#line 159 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy24 = NCDConfig_make_arguments_string(yymsp[-2].minor.yy0, yymsp[0].minor.yy24);
-    if (!yygotominor.yy24) {
+    yygotominor.yy8 = NCDConfig_make_arguments_string(yymsp[-2].minor.yy0, yymsp[0].minor.yy8);
+    if (!yygotominor.yy8) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,11,&yymsp[-1].minor);
+  yy_destructor(yypParser,10,&yymsp[-1].minor);
 }
-#line 907 "NCDConfigParser_parse.c"
+#line 912 "NCDConfigParser_parse.c"
         break;
       case 14: /* statement_args ::= statement_names COMMA statement_args */
-#line 165 "NCDConfigParser_parse.y"
+#line 166 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy24 = NCDConfig_make_arguments_var(yymsp[-2].minor.yy4, yymsp[0].minor.yy24);
-    if (!yygotominor.yy24) {
+    yygotominor.yy8 = NCDConfig_make_arguments_var(yymsp[-2].minor.yy40, yymsp[0].minor.yy8);
+    if (!yygotominor.yy8) {
         parser_out->out_of_memory = 1;
     }
-  yy_destructor(yypParser,11,&yymsp[-1].minor);
+  yy_destructor(yypParser,10,&yymsp[-1].minor);
 }
-#line 918 "NCDConfigParser_parse.c"
+#line 923 "NCDConfigParser_parse.c"
         break;
       case 15: /* name_maybe ::= */
-#line 172 "NCDConfigParser_parse.y"
+#line 173 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy33 = NULL;
+    yygotominor.yy9 = NULL;
 }
-#line 925 "NCDConfigParser_parse.c"
+#line 930 "NCDConfigParser_parse.c"
         break;
       case 16: /* name_maybe ::= NAME */
-#line 176 "NCDConfigParser_parse.y"
+#line 177 "NCDConfigParser_parse.y"
+{
+    yygotominor.yy9 = yymsp[0].minor.yy0;
+}
+#line 937 "NCDConfigParser_parse.c"
+        break;
+      case 17: /* process_or_template ::= PROCESS */
+#line 181 "NCDConfigParser_parse.y"
+{
+    yygotominor.yy20 = 0;
+  yy_destructor(yypParser,11,&yymsp[0].minor);
+}
+#line 945 "NCDConfigParser_parse.c"
+        break;
+      case 18: /* process_or_template ::= TEMPLATE */
+#line 185 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy33 = yymsp[0].minor.yy0;
+    yygotominor.yy20 = 1;
+  yy_destructor(yypParser,12,&yymsp[0].minor);
 }
-#line 932 "NCDConfigParser_parse.c"
+#line 953 "NCDConfigParser_parse.c"
         break;
       default:
         break;
@@ -990,10 +1011,10 @@ static void yy_syntax_error(
 ){
   ParseARG_FETCH;
 #define TOKEN (yyminor.yy0)
-#line 61 "NCDConfigParser_parse.y"
+#line 62 "NCDConfigParser_parse.y"
 
     parser_out->syntax_error = 1;
-#line 997 "NCDConfigParser_parse.c"
+#line 1018 "NCDConfigParser_parse.c"
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
 

+ 12 - 11
generated/NCDConfigParser_parse.h

@@ -1,11 +1,12 @@
-#define PROCESS                         1
-#define NAME                            2
-#define CURLY_OPEN                      3
-#define CURLY_CLOSE                     4
-#define ROUND_OPEN                      5
-#define ROUND_CLOSE                     6
-#define SEMICOLON                       7
-#define ARROW                           8
-#define DOT                             9
-#define STRING                         10
-#define COMMA                          11
+#define NAME                            1
+#define CURLY_OPEN                      2
+#define CURLY_CLOSE                     3
+#define ROUND_OPEN                      4
+#define ROUND_CLOSE                     5
+#define SEMICOLON                       6
+#define ARROW                           7
+#define DOT                             8
+#define STRING                          9
+#define COMMA                          10
+#define PROCESS                        11
+#define TEMPLATE                       12

+ 86 - 66
generated/NCDConfigParser_parse.out

@@ -1,10 +1,14 @@
 State 0:
           input ::= * interfaces
-          interfaces ::= * PROCESS NAME CURLY_OPEN statements CURLY_CLOSE
-          interfaces ::= * PROCESS NAME CURLY_OPEN statements CURLY_CLOSE interfaces
+          interfaces ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE
+          interfaces ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE interfaces
+          process_or_template ::= * PROCESS
+          process_or_template ::= * TEMPLATE
 
-                       PROCESS shift  15
+                       PROCESS shift  27
+                      TEMPLATE shift  28
                     interfaces shift  14
+           process_or_template shift  15
                          input accept
 
 State 1:
@@ -23,7 +27,7 @@ State 1:
                         STRING shift  21
                statement_names shift  22
           statement_args_maybe shift  18
-                statement_args shift  30
+                statement_args shift  32
                      {default} reduce 9
 
 State 2:
@@ -42,10 +46,24 @@ State 2:
                         STRING shift  21
                statement_names shift  22
           statement_args_maybe shift  24
-                statement_args shift  30
+                statement_args shift  32
                      {default} reduce 9
 
 State 3:
+          interfaces ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE
+      (1) interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE *
+          interfaces ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE interfaces
+          interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE * interfaces
+          process_or_template ::= * PROCESS
+          process_or_template ::= * TEMPLATE
+
+                       PROCESS shift  27
+                      TEMPLATE shift  28
+                    interfaces shift  26
+           process_or_template shift  15
+                     {default} reduce 1
+
+State 4:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
           statement_args ::= * STRING
@@ -57,9 +75,9 @@ State 3:
                           NAME shift  20
                         STRING shift  21
                statement_names shift  22
-                statement_args shift  32
+                statement_args shift  34
 
-State 4:
+State 5:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
           statement_args ::= * STRING
@@ -71,11 +89,11 @@ State 4:
                           NAME shift  20
                         STRING shift  21
                statement_names shift  22
-                statement_args shift  31
+                statement_args shift  33
 
-State 5:
-          interfaces ::= PROCESS NAME CURLY_OPEN * statements CURLY_CLOSE
-          interfaces ::= PROCESS NAME CURLY_OPEN * statements CURLY_CLOSE interfaces
+State 6:
+          interfaces ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE
+          interfaces ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE interfaces
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
           statements ::= * statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
@@ -87,7 +105,7 @@ State 5:
                     statements shift  17
                statement_names shift  13
 
-State 6:
+State 7:
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
       (3) statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON *
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
@@ -98,11 +116,11 @@ State 6:
           statement_names ::= * NAME DOT statement_names
 
                           NAME shift  20
-                    statements shift  27
+                    statements shift  29
                statement_names shift  13
                      {default} reduce 3
 
-State 7:
+State 8:
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
           statements ::= * statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
@@ -113,27 +131,17 @@ State 7:
           statement_names ::= * NAME DOT statement_names
 
                           NAME shift  20
-                    statements shift  33
+                    statements shift  35
                statement_names shift  13
                      {default} reduce 5
 
-State 8:
-          interfaces ::= * PROCESS NAME CURLY_OPEN statements CURLY_CLOSE
-      (1) interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE *
-          interfaces ::= * PROCESS NAME CURLY_OPEN statements CURLY_CLOSE interfaces
-          interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE * interfaces
-
-                       PROCESS shift  15
-                    interfaces shift  26
-                     {default} reduce 1
-
 State 9:
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON statements
      (15) name_maybe ::= *
           name_maybe ::= * NAME
 
-                          NAME shift  29
+                          NAME shift  31
                     name_maybe shift  19
                      {default} reduce 15
 
@@ -143,7 +151,7 @@ State 10:
           statement_names ::= NAME DOT * statement_names
 
                           NAME shift  20
-               statement_names shift  28
+               statement_names shift  30
 
 State 11:
           statements ::= statement_names ARROW * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
@@ -160,7 +168,7 @@ State 12:
      (15) name_maybe ::= *
           name_maybe ::= * NAME
 
-                          NAME shift  29
+                          NAME shift  31
                     name_maybe shift  25
                      {default} reduce 15
 
@@ -179,22 +187,22 @@ State 14:
                              $ reduce 0
 
 State 15:
-          interfaces ::= PROCESS * NAME CURLY_OPEN statements CURLY_CLOSE
-          interfaces ::= PROCESS * NAME CURLY_OPEN statements CURLY_CLOSE interfaces
+          interfaces ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE
+          interfaces ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE interfaces
 
                           NAME shift  16
 
 State 16:
-          interfaces ::= PROCESS NAME * CURLY_OPEN statements CURLY_CLOSE
-          interfaces ::= PROCESS NAME * CURLY_OPEN statements CURLY_CLOSE interfaces
+          interfaces ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE
+          interfaces ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE interfaces
 
-                    CURLY_OPEN shift  5
+                    CURLY_OPEN shift  6
 
 State 17:
-          interfaces ::= PROCESS NAME CURLY_OPEN statements * CURLY_CLOSE
-          interfaces ::= PROCESS NAME CURLY_OPEN statements * CURLY_CLOSE interfaces
+          interfaces ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE
+          interfaces ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE interfaces
 
-                   CURLY_CLOSE shift  8
+                   CURLY_CLOSE shift  3
 
 State 18:
           statements ::= statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
@@ -206,7 +214,7 @@ State 19:
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON statements
 
-                     SEMICOLON shift  6
+                     SEMICOLON shift  7
 
 State 20:
       (7) statement_names ::= NAME *
@@ -219,14 +227,14 @@ State 21:
      (11) statement_args ::= STRING *
           statement_args ::= STRING * COMMA statement_args
 
-                         COMMA shift  3
+                         COMMA shift  4
                      {default} reduce 11
 
 State 22:
      (12) statement_args ::= statement_names *
           statement_args ::= statement_names * COMMA statement_args
 
-                         COMMA shift  4
+                         COMMA shift  5
                      {default} reduce 12
 
 State 23:
@@ -245,44 +253,54 @@ State 25:
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON statements
 
-                     SEMICOLON shift  7
+                     SEMICOLON shift  8
 
 State 26:
-      (2) interfaces ::= PROCESS NAME CURLY_OPEN statements CURLY_CLOSE interfaces *
+      (2) interfaces ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE interfaces *
 
                      {default} reduce 2
 
 State 27:
+     (17) process_or_template ::= PROCESS *
+
+                     {default} reduce 17
+
+State 28:
+     (18) process_or_template ::= TEMPLATE *
+
+                     {default} reduce 18
+
+State 29:
       (4) statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements *
 
                      {default} reduce 4
 
-State 28:
+State 30:
       (8) statement_names ::= NAME DOT statement_names *
 
                      {default} reduce 8
 
-State 29:
+State 31:
      (16) name_maybe ::= NAME *
 
                      {default} reduce 16
 
-State 30:
+State 32:
      (10) statement_args_maybe ::= statement_args *
 
                      {default} reduce 10
 
-State 31:
+State 33:
      (14) statement_args ::= statement_names COMMA statement_args *
 
                      {default} reduce 14
 
-State 32:
+State 34:
      (13) statement_args ::= STRING COMMA statement_args *
 
                      {default} reduce 13
 
-State 33:
+State 35:
       (6) statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements *
 
                      {default} reduce 6
@@ -290,22 +308,24 @@ State 33:
 ----------------------------------------------------
 Symbols:
     0: $:
-    1: PROCESS
-    2: NAME
-    3: CURLY_OPEN
-    4: CURLY_CLOSE
-    5: ROUND_OPEN
-    6: ROUND_CLOSE
-    7: SEMICOLON
-    8: ARROW
-    9: DOT
-   10: STRING
-   11: COMMA
-   12: error:
-   13: interfaces: PROCESS
-   14: statements: NAME
-   15: statement_names: NAME
-   16: statement_args_maybe: <lambda> NAME STRING
-   17: statement_args: NAME STRING
-   18: name_maybe: <lambda> NAME
-   19: input: PROCESS
+    1: NAME
+    2: CURLY_OPEN
+    3: CURLY_CLOSE
+    4: ROUND_OPEN
+    5: ROUND_CLOSE
+    6: SEMICOLON
+    7: ARROW
+    8: DOT
+    9: STRING
+   10: COMMA
+   11: PROCESS
+   12: TEMPLATE
+   13: error:
+   14: interfaces: PROCESS TEMPLATE
+   15: statements: NAME
+   16: statement_names: NAME
+   17: statement_args_maybe: <lambda> NAME STRING
+   18: statement_args: NAME STRING
+   19: name_maybe: <lambda> NAME
+   20: process_or_template: PROCESS TEMPLATE
+   21: input: PROCESS TEMPLATE

+ 13 - 4
generated/NCDConfigParser_parse.y

@@ -48,6 +48,7 @@ struct parser_out {
 %type statement_args_maybe {struct NCDConfig_arguments *}
 %type statement_args {struct NCDConfig_arguments *}
 %type name_maybe {char *}
+%type process_or_template {int}
 
 %destructor interfaces { NCDConfig_free_interfaces($$); }
 %destructor statements { NCDConfig_free_statements($$); }
@@ -77,15 +78,15 @@ input ::= interfaces(A). {
     }
 }
 
-interfaces(R) ::= PROCESS NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
-    R = NCDConfig_make_interfaces(A, B, 0, NULL);
+interfaces(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
+    R = NCDConfig_make_interfaces(T, A, B, 0, NULL);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-interfaces(R) ::= PROCESS NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE interfaces(N). {
-    R = NCDConfig_make_interfaces(A, B, 1, N);
+interfaces(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE interfaces(N). {
+    R = NCDConfig_make_interfaces(T, A, B, 1, N);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
@@ -176,3 +177,11 @@ name_maybe(R) ::= . {
 name_maybe(R) ::= NAME(A). {
     R = A;
 }
+
+process_or_template(R) ::= PROCESS. {
+    R = 0;
+}
+
+process_or_template(R) ::= TEMPLATE. {
+    R = 1;
+}

+ 4 - 0
generated/blog_channel_ncd_synchronous_process.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_synchronous_process

+ 41 - 40
generated/blog_channels_defines.h

@@ -18,43 +18,44 @@
 #define BLOG_CHANNEL_ncd_blocker 17
 #define BLOG_CHANNEL_ncd_ip_in_network 18
 #define BLOG_CHANNEL_ncd_run 19
-#define BLOG_CHANNEL_ncd_net_backend_waitdevice 20
-#define BLOG_CHANNEL_ncd_net_backend_waitlink 21
-#define BLOG_CHANNEL_ncd_net_backend_badvpn 22
-#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 23
-#define BLOG_CHANNEL_ncd_net_backend_rfkill 24
-#define BLOG_CHANNEL_ncd_net_up 25
-#define BLOG_CHANNEL_ncd_net_dns 26
-#define BLOG_CHANNEL_ncd_net_iptables 27
-#define BLOG_CHANNEL_ncd_net_ipv4_addr 28
-#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 29
-#define BLOG_CHANNEL_ncd_net_ipv4_route 30
-#define BLOG_CHANNEL_ncd_sys_evdev 31
-#define BLOG_CHANNEL_StreamPeerIO 32
-#define BLOG_CHANNEL_DatagramPeerIO 33
-#define BLOG_CHANNEL_BReactor 34
-#define BLOG_CHANNEL_BSignal 35
-#define BLOG_CHANNEL_FragmentProtoAssembler 36
-#define BLOG_CHANNEL_BPredicate 37
-#define BLOG_CHANNEL_ServerConnection 38
-#define BLOG_CHANNEL_Listener 39
-#define BLOG_CHANNEL_DataProto 40
-#define BLOG_CHANNEL_FrameDecider 41
-#define BLOG_CHANNEL_BSocksClient 42
-#define BLOG_CHANNEL_BDHCPClientCore 43
-#define BLOG_CHANNEL_BDHCPClient 44
-#define BLOG_CHANNEL_NCDIfConfig 45
-#define BLOG_CHANNEL_BUnixSignal 46
-#define BLOG_CHANNEL_BProcess 47
-#define BLOG_CHANNEL_StreamSocketSink 48
-#define BLOG_CHANNEL_StreamSocketSource 49
-#define BLOG_CHANNEL_DatagramSocketSink 50
-#define BLOG_CHANNEL_DatagramSocketSource 51
-#define BLOG_CHANNEL_PRStreamSink 52
-#define BLOG_CHANNEL_PRStreamSource 53
-#define BLOG_CHANNEL_BSocketPRFileDesc 54
-#define BLOG_CHANNEL_PacketProtoDecoder 55
-#define BLOG_CHANNEL_DPRelay 56
-#define BLOG_CHANNEL_BThreadWork 57
-#define BLOG_CHANNEL_DPReceive 58
-#define BLOG_NUM_CHANNELS 59
+#define BLOG_CHANNEL_ncd_synchronous_process 20
+#define BLOG_CHANNEL_ncd_net_backend_waitdevice 21
+#define BLOG_CHANNEL_ncd_net_backend_waitlink 22
+#define BLOG_CHANNEL_ncd_net_backend_badvpn 23
+#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 24
+#define BLOG_CHANNEL_ncd_net_backend_rfkill 25
+#define BLOG_CHANNEL_ncd_net_up 26
+#define BLOG_CHANNEL_ncd_net_dns 27
+#define BLOG_CHANNEL_ncd_net_iptables 28
+#define BLOG_CHANNEL_ncd_net_ipv4_addr 29
+#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 30
+#define BLOG_CHANNEL_ncd_net_ipv4_route 31
+#define BLOG_CHANNEL_ncd_sys_evdev 32
+#define BLOG_CHANNEL_StreamPeerIO 33
+#define BLOG_CHANNEL_DatagramPeerIO 34
+#define BLOG_CHANNEL_BReactor 35
+#define BLOG_CHANNEL_BSignal 36
+#define BLOG_CHANNEL_FragmentProtoAssembler 37
+#define BLOG_CHANNEL_BPredicate 38
+#define BLOG_CHANNEL_ServerConnection 39
+#define BLOG_CHANNEL_Listener 40
+#define BLOG_CHANNEL_DataProto 41
+#define BLOG_CHANNEL_FrameDecider 42
+#define BLOG_CHANNEL_BSocksClient 43
+#define BLOG_CHANNEL_BDHCPClientCore 44
+#define BLOG_CHANNEL_BDHCPClient 45
+#define BLOG_CHANNEL_NCDIfConfig 46
+#define BLOG_CHANNEL_BUnixSignal 47
+#define BLOG_CHANNEL_BProcess 48
+#define BLOG_CHANNEL_StreamSocketSink 49
+#define BLOG_CHANNEL_StreamSocketSource 50
+#define BLOG_CHANNEL_DatagramSocketSink 51
+#define BLOG_CHANNEL_DatagramSocketSource 52
+#define BLOG_CHANNEL_PRStreamSink 53
+#define BLOG_CHANNEL_PRStreamSource 54
+#define BLOG_CHANNEL_BSocketPRFileDesc 55
+#define BLOG_CHANNEL_PacketProtoDecoder 56
+#define BLOG_CHANNEL_DPRelay 57
+#define BLOG_CHANNEL_BThreadWork 58
+#define BLOG_CHANNEL_DPReceive 59
+#define BLOG_NUM_CHANNELS 60

+ 1 - 0
generated/blog_channels_list.h

@@ -18,6 +18,7 @@
 {.name = "ncd_blocker", .loglevel = 4},
 {.name = "ncd_ip_in_network", .loglevel = 4},
 {.name = "ncd_run", .loglevel = 4},
+{.name = "ncd_synchronous_process", .loglevel = 4},
 {.name = "ncd_net_backend_waitdevice", .loglevel = 4},
 {.name = "ncd_net_backend_waitlink", .loglevel = 4},
 {.name = "ncd_net_backend_badvpn", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -37,6 +37,7 @@ add_executable(badvpn-ncd
     modules/blocker.c
     modules/ip_in_network.c
     modules/run.c
+    modules/synchronous_process.c
     modules/net_backend_waitdevice.c
     modules/net_backend_waitlink.c
     modules/net_backend_badvpn.c

+ 141 - 3
ncd/NCDModule.c

@@ -36,6 +36,14 @@
 #define STATE_DYING 10
 #define STATE_UNDEAD 11
 
+#define PROCESS_STATE_INIT 1
+#define PROCESS_STATE_NORMAL 2
+#define PROCESS_STATE_DIE_PENDING 3
+#define PROCESS_STATE_DIE 4
+#define PROCESS_STATE_DEAD_PENDING 5
+#define PROCESS_STATE_DEAD 6
+#define PROCESS_STATE_ZOMBIE 7
+
 static void frontend_event (NCDModuleInst *n, int event)
 {
     n->handler_event(n->user, event);
@@ -88,8 +96,35 @@ static void clean_job_handler (NCDModuleInst *n)
     }
 }
 
-void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleInst *method_object, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager,
-                         NCDModule_handler_event handler_event, NCDModule_handler_getvar handler_getvar, NCDModule_handler_getobj handler_getobj, void *user)
+static void process_die_job_handler (NCDModuleProcess *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->state == PROCESS_STATE_DIE_PENDING)
+    
+    // set state
+    o->state = PROCESS_STATE_DIE;
+    
+    o->interp_handler_die(o->interp_user);
+    return;
+}
+
+static void process_dead_job_handler (NCDModuleProcess *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->state == PROCESS_STATE_DEAD_PENDING)
+    
+    // set state
+    o->state = PROCESS_STATE_DEAD;
+    
+    o->handler_dead(o->user);
+    return;
+}
+
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleInst *method_object, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager, void *user,
+                         NCDModule_handler_event handler_event,
+                         NCDModule_handler_getvar handler_getvar,
+                         NCDModule_handler_getobj handler_getobj,
+                         NCDModule_handler_initprocess handler_initprocess)
 {
     // init arguments
     n->m = m;
@@ -98,10 +133,11 @@ void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleI
     n->logprefix = logprefix;
     n->reactor = reactor;
     n->manager = manager;
+    n->user = user;
     n->handler_event = handler_event;
     n->handler_getvar = handler_getvar;
     n->handler_getobj = handler_getobj;
-    n->user = user;
+    n->handler_initprocess = handler_initprocess;
     
     // init jobs
     BPending_Init(&n->init_job, BReactor_PendingGroup(n->reactor), (BPending_handler)init_job_handler, n);
@@ -343,3 +379,105 @@ void NCDModuleInst_Backend_SetError (NCDModuleInst *n)
     
     n->is_error = 1;
 }
+
+int NCDModuleProcess_Init (NCDModuleProcess *o, NCDModuleInst *n, const char *template_name, NCDValue args, void *user, NCDModuleProcess_handler_dead handler_dead)
+{
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DOWN_PCLEAN || n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
+           n->state == STATE_UP || n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE ||
+           n->state == STATE_DYING)
+    ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST)
+    
+    // init arguments
+    o->n = n;
+    o->user = user;
+    o->handler_dead = handler_dead;
+    
+    // clear interpreter data
+    o->interp_user = NULL;
+    o->interp_handler_die = NULL;
+    
+    // init jobs
+    BPending_Init(&o->die_job, BReactor_PendingGroup(n->reactor), (BPending_handler)process_die_job_handler, o);
+    BPending_Init(&o->dead_job, BReactor_PendingGroup(n->reactor), (BPending_handler)process_dead_job_handler, o);
+    
+    // set state
+    o->state = PROCESS_STATE_INIT;
+    
+    // init interpreter part
+    if (!(n->handler_initprocess(n->user, o, template_name, args))) {
+        goto fail0;
+    }
+    
+    // set state
+    o->state = PROCESS_STATE_NORMAL;
+    
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail0:
+    BPending_Free(&o->dead_job);
+    BPending_Free(&o->die_job);
+    return 0;
+}
+
+void NCDModuleProcess_Free (NCDModuleProcess *o)
+{
+    DebugObject_Free(&o->d_obj);
+    ASSERT(o->state == PROCESS_STATE_DEAD)
+    
+    // free jobs
+    BPending_Free(&o->dead_job);
+    BPending_Free(&o->die_job);
+}
+
+void NCDModuleProcess_Die (NCDModuleProcess *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    switch (o->state) {
+        case PROCESS_STATE_ZOMBIE: {
+            BPending_Set(&o->dead_job);
+            
+            o->state = PROCESS_STATE_DEAD_PENDING;
+        } break;
+        
+        case PROCESS_STATE_NORMAL: {
+            BPending_Set(&o->die_job);
+        
+            o->state = PROCESS_STATE_DIE_PENDING;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user, NCDModuleProcess_interp_handler_die interp_handler_die)
+{
+    o->interp_user = interp_user;
+    o->interp_handler_die = interp_handler_die;
+}
+
+void NCDModuleProcess_Interp_Dead (NCDModuleProcess *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    switch (o->state) {
+        case PROCESS_STATE_NORMAL: {
+            o->state = PROCESS_STATE_ZOMBIE;
+        } break;
+        
+        case PROCESS_STATE_DIE_PENDING: {
+            BPending_Unset(&o->die_job);
+            BPending_Set(&o->dead_job);
+            o->state = PROCESS_STATE_DEAD_PENDING;
+        } break;
+        
+        case PROCESS_STATE_DIE: {
+            BPending_Set(&o->dead_job);
+            o->state = PROCESS_STATE_DEAD_PENDING;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}

+ 30 - 3
ncd/NCDModule.h

@@ -38,10 +38,15 @@
 #define NCDMODULE_TOEVENT_CLEAN 102
 
 struct NCDModuleInst_s;
+struct NCDModuleProcess_s;
 
 typedef void (*NCDModule_handler_event) (void *user, int event);
 typedef int (*NCDModule_handler_getvar) (void *user, const char *modname, const char *varname, NCDValue *out);
 typedef struct NCDModuleInst_s * (*NCDModule_handler_getobj) (void *user, const char *objname);
+typedef int (*NCDModule_handler_initprocess) (void *user, struct NCDModuleProcess_s *p, const char *template_name, NCDValue args);
+
+typedef void (*NCDModuleProcess_handler_dead) (void *user);
+typedef void (*NCDModuleProcess_interp_handler_die) (void *user);
 
 struct NCDModule;
 
@@ -57,10 +62,11 @@ typedef struct NCDModuleInst_s {
     const char *logprefix;
     BReactor *reactor;
     BProcessManager *manager;
+    void *user;
     NCDModule_handler_event handler_event;
     NCDModule_handler_getvar handler_getvar;
     NCDModule_handler_getobj handler_getobj;
-    void *user;
+    NCDModule_handler_initprocess handler_initprocess;
     BPending init_job;
     BPending uninit_job;
     BPending die_job;
@@ -71,8 +77,23 @@ typedef struct NCDModuleInst_s {
     DebugObject d_obj;
 } NCDModuleInst;
 
-void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleInst *method_object, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager,
-                         NCDModule_handler_event handler_event, NCDModule_handler_getvar handler_getvar, NCDModule_handler_getobj handler_getobj, void *user);
+typedef struct NCDModuleProcess_s {
+    NCDModuleInst *n;
+    void *user;
+    NCDModuleProcess_handler_dead handler_dead;
+    void *interp_user;
+    NCDModuleProcess_interp_handler_die interp_handler_die;
+    BPending die_job;
+    BPending dead_job;
+    int state;
+    DebugObject d_obj;
+} NCDModuleProcess;
+
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleInst *method_object, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager, void *user,
+                         NCDModule_handler_event handler_event,
+                         NCDModule_handler_getvar handler_getvar,
+                         NCDModule_handler_getobj handler_getobj,
+                         NCDModule_handler_initprocess handler_initprocess);
 void NCDModuleInst_Free (NCDModuleInst *n);
 void NCDModuleInst_Event (NCDModuleInst *n, int event);
 int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out) WARN_UNUSED;
@@ -85,6 +106,12 @@ NCDModuleInst * NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *objn
 void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...);
 void NCDModuleInst_Backend_SetError (NCDModuleInst *n);
 
+int NCDModuleProcess_Init (NCDModuleProcess *o, NCDModuleInst *n, const char *template_name, NCDValue args, void *user, NCDModuleProcess_handler_dead handler_dead);
+void NCDModuleProcess_Free (NCDModuleProcess *o);
+void NCDModuleProcess_Die (NCDModuleProcess *o);
+void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user, NCDModuleProcess_interp_handler_die interp_handler_die);
+void NCDModuleProcess_Interp_Dead (NCDModuleProcess *o);
+
 typedef int (*NCDModule_func_globalinit) (const struct NCDModuleInitParams params);
 typedef void (*NCDModule_func_globalfree) (void);
 typedef void (*NCDModule_func_new) (NCDModuleInst *params);

+ 2 - 0
ncd/modules/modules.h

@@ -42,6 +42,7 @@ extern const struct NCDModuleGroup ncdmodule_print;
 extern const struct NCDModuleGroup ncdmodule_blocker;
 extern const struct NCDModuleGroup ncdmodule_ip_in_network;
 extern const struct NCDModuleGroup ncdmodule_run;
+extern const struct NCDModuleGroup ncdmodule_synchronous_process;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitlink;
 extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn;
@@ -75,6 +76,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_blocker,
     &ncdmodule_ip_in_network,
     &ncdmodule_run,
+    &ncdmodule_synchronous_process,
     &ncdmodule_net_backend_waitdevice,
     &ncdmodule_net_backend_waitlink,
     &ncdmodule_net_backend_badvpn,

+ 139 - 0
ncd/modules/synchronous_process.c

@@ -0,0 +1,139 @@
+/**
+ * @file synchronous_process.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * @section DESCRIPTION
+ * 
+ * Module which starts a process from a process template on initialization, and
+ * stops it on deinitialization.
+ * 
+ * Synopsis: synchronous_process(string template_name, list(string) args)
+ * Description: on initialization, creates a new process from the template named
+ *   template_name, with arguments args. On deinitialization, initiates termination
+ *   of the process and waits for it to terminate.
+ */
+
+#include <stdlib.h>
+
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_synchronous_process.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    NCDModuleProcess process;
+};
+
+static void instance_free (struct instance *o);
+
+static void process_handler_dead (struct instance *o)
+{
+    // die now
+    instance_free(o);
+}
+
+static void func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct instance *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // check arguments
+    NCDValue *template_name_arg;
+    NCDValue *args_arg;
+    if (!NCDValue_ListRead(o->i->args, 2, &template_name_arg, &args_arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(template_name_arg) != NCDVALUE_STRING || NCDValue_Type(args_arg) != NCDVALUE_LIST) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    
+    // signal up.
+    // Do it before creating the process so that the process starts initializing before our own process continues.
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
+    // copy arguments
+    NCDValue args;
+    if (!NCDValue_InitCopy(&args, args_arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
+        goto fail1;
+    }
+    
+    // create process
+    if (!NCDModuleProcess_Init(&o->process, o->i, NCDValue_StringValue(template_name_arg), args, o, (NCDModuleProcess_handler_dead)process_handler_dead)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
+        NCDValue_Free(&args);
+        goto fail1;
+    }
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+void instance_free (struct instance *o)
+{
+    NCDModuleInst *i = o->i;
+    
+    // free process
+    NCDModuleProcess_Free(&o->process);
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    
+    // request process to die
+    NCDModuleProcess_Die(&o->process);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "synchronous_process",
+        .func_new = func_new,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_synchronous_process = {
+    .modules = modules
+};

+ 124 - 18
ncd/ncd.c

@@ -32,6 +32,8 @@
 #include <misc/read_file.h>
 #include <misc/balloc.h>
 #include <misc/concat_strings.h>
+#include <misc/string_begins_with.h>
+#include <misc/parse_number.h>
 #include <structure/LinkedList2.h>
 #include <system/BLog.h>
 #include <system/BReactor.h>
@@ -78,9 +80,12 @@ struct argument_elem {
 };
 
 struct process {
+    NCDModuleProcess *module_process;
+    NCDValue args;
     char *name;
     size_t num_statements;
     struct process_statement *statements;
+    int terminating;
     size_t ap;
     size_t fp;
     BTimer wait_timer;
@@ -140,8 +145,9 @@ static const struct NCDModule * find_module (const char *name);
 static int statement_init (struct statement *s, struct NCDConfig_statements *conf);
 static void statement_free (struct statement *s);
 static void statement_free_args (struct statement *s);
-static int process_new (struct NCDConfig_interfaces *conf);
+static int process_new (struct NCDConfig_interfaces *conf, NCDModuleProcess *module_process, NCDValue args);
 static void process_free (struct process *p);
+static void process_start_teminating (struct process *p);
 static void process_free_statements (struct process *p);
 static size_t process_rap (struct process *p);
 static void process_assert_pointers (struct process *p);
@@ -158,6 +164,8 @@ static void process_statement_set_error (struct process_statement *ps);
 static void process_statement_instance_handler_event (struct process_statement *ps, int event);
 static int process_statement_instance_handler_getvar (struct process_statement *ps, const char *modname, const char *varname, NCDValue *out);
 static NCDModuleInst * process_statement_instance_handler_getobj (struct process_statement *ps, const char *objname);
+static int process_statement_instance_handler_initprocess (struct process_statement *ps, NCDModuleProcess *mp, const char *template_name, NCDValue args);
+static void process_moduleprocess_handler_die (struct process *p);
 
 int main (int argc, char **argv)
 {
@@ -281,7 +289,13 @@ int main (int argc, char **argv)
     // init processes
     struct NCDConfig_interfaces *conf = config_ast;
     while (conf) {
-        process_new(conf);
+        if (!conf->is_template) {
+            NCDValue args;
+            NCDValue_InitList(&args);
+            if (!process_new(conf, NULL, args)) {
+                NCDValue_Free(&args);
+            }
+        }
         conf = conf->next;
     }
     
@@ -289,12 +303,8 @@ int main (int argc, char **argv)
     BLog(BLOG_NOTICE, "entering event loop");
     BReactor_Exec(&ss);
     
-    // free processes
-    LinkedList2Node *n;
-    while (n = LinkedList2_GetFirst(&processes)) {
-        struct process *p = UPPER_OBJECT(n, struct process, list_node);
-        process_free(p);
-    }
+    ASSERT(LinkedList2_IsEmpty(&processes))
+    
 fail5:
     // free modules
     while (num_inited_modules > 0) {
@@ -500,13 +510,13 @@ void signal_handler (void *unused)
         return;
     }
     
-    // schedule work for all processes
+    // start terminating all processes
     LinkedList2Iterator it;
     LinkedList2Iterator_InitForward(&it, &processes);
     LinkedList2Node *n;
     while (n = LinkedList2Iterator_Next(&it)) {
         struct process *p = UPPER_OBJECT(n, struct process, list_node);
-        process_schedule_work(p);
+        process_start_teminating(p);
     }
 }
 
@@ -641,8 +651,10 @@ void statement_free_args (struct statement *s)
     }
 }
 
-int process_new (struct NCDConfig_interfaces *conf)
+int process_new (struct NCDConfig_interfaces *conf, NCDModuleProcess *module_process, NCDValue args)
 {
+    ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST)
+    
     // allocate strucure
     struct process *p = malloc(sizeof(*p));
     if (!p) {
@@ -650,6 +662,17 @@ int process_new (struct NCDConfig_interfaces *conf)
         goto fail0;
     }
     
+    // set module process
+    p->module_process = module_process;
+    
+    // set module process handlers
+    if (p->module_process) {
+        NCDModuleProcess_Interp_SetHandlers(p->module_process, p, (NCDModuleProcess_interp_handler_die)process_moduleprocess_handler_die);
+    }
+    
+    // set arguments
+    p->args = args;
+    
     // init name
     if (!(p->name = strdup(conf->name))) {
         BLog(BLOG_ERROR, "strdup failed");
@@ -695,6 +718,9 @@ int process_new (struct NCDConfig_interfaces *conf)
         st = st->next;
     }
     
+    // set not terminating
+    p->terminating = 0;
+    
     // set AP=0
     p->ap = 0;
     
@@ -734,6 +760,11 @@ void process_free (struct process *p)
     ASSERT(p->ap == 0)
     ASSERT(p->fp == 0)
     
+    // inform module process that the process is dead
+    if (p->module_process) {
+        NCDModuleProcess_Interp_Dead(p->module_process);
+    }
+    
     // remove from processes list
     LinkedList2_Remove(&processes, &p->list_node);
     
@@ -752,10 +783,26 @@ void process_free (struct process *p)
     // free name
     free(p->name);
     
+    // free arguments
+    NCDValue_Free(&p->args);
+    
     // free strucure
     free(p);
 }
 
+void process_start_teminating (struct process *p)
+{
+    if (p->terminating) {
+        return;
+    }
+    
+    // set terminating
+    p->terminating = 1;
+    
+    // schedule work
+    process_schedule_work(p);
+}
+
 size_t process_rap (struct process *p)
 {
     if (p->ap > 0 && p->statements[p->ap - 1].state == SSTATE_CHILD) {
@@ -829,13 +876,13 @@ void process_work_job_handler (struct process *p)
     ASSERT(!BTimer_IsRunning(&p->wait_timer))
     ASSERT(!BPending_IsSet(&p->advance_job))
     
-    if (terminating) {
+    if (p->terminating) {
         if (p->fp == 0) {
             // finished retreating
             process_free(p);
             
-            // if there are no more processes, exit program
-            if (LinkedList2_IsEmpty(&processes)) {
+            // if program is terminating amd there are no more processes, exit program
+            if (terminating && LinkedList2_IsEmpty(&processes)) {
                 BReactor_Quit(&ss, 1);
             }
         } else {
@@ -924,7 +971,7 @@ void process_advance_job_handler (struct process *p)
     ASSERT(!p->statements[p->ap].have_error)
     ASSERT(!BPending_IsSet(&p->work_job))
     ASSERT(!BTimer_IsRunning(&p->wait_timer))
-    ASSERT(!terminating)
+    ASSERT(!p->terminating)
     
     struct process_statement *ps = &p->statements[p->ap];
     ASSERT(ps->state == SSTATE_FORGOTTEN)
@@ -1002,11 +1049,11 @@ void process_advance_job_handler (struct process *p)
     
     // initialize module instance
     NCDModuleInst_Init(
-        &ps->inst, module, method_object, &ps->inst_args, ps->logprefix, &ss, &manager,
+        &ps->inst, module, method_object, &ps->inst_args, ps->logprefix, &ss, &manager, ps,
         (NCDModule_handler_event)process_statement_instance_handler_event,
         (NCDModule_handler_getvar)process_statement_instance_handler_getvar,
         (NCDModule_handler_getobj)process_statement_instance_handler_getobj,
-        ps
+        (NCDModule_handler_initprocess)process_statement_instance_handler_initprocess
     );
     
     // set statement state CHILD
@@ -1042,7 +1089,7 @@ void process_wait_timer_handler (struct process *p)
     ASSERT(p->statements[p->ap].have_error)
     ASSERT(!BPending_IsSet(&p->work_job))
     ASSERT(!BPending_IsSet(&p->advance_job))
-    ASSERT(!terminating)
+    ASSERT(!p->terminating)
     
     process_log(p, BLOG_INFO, "retrying");
     
@@ -1080,6 +1127,28 @@ int process_resolve_variable (struct process *p, size_t pos, const char *modname
     // find referred-to statement
     struct process_statement *rps = process_find_statement(p, pos, modname);
     if (!rps) {
+        // handle _args
+        if (!strcmp(modname, "_args") && !strcmp(varname, "")) {
+            if (!NCDValue_InitCopy(out, &p->args)) {
+                process_log(p, BLOG_ERROR, "NCDValue_InitCopy failed");
+                return 0;
+            }
+            
+            return 1;
+        }
+        
+        // handle _argN
+        size_t len;
+        uintmax_t n;
+        if ((len = string_begins_with(modname, "_arg")) && parse_unsigned_integer(modname + len, &n) && n < NCDValue_ListCount(&p->args) && !strcmp(varname, "")) {
+            if (!NCDValue_InitCopy(out, NCDValue_ListGet(&p->args, n))) {
+                process_log(p, BLOG_ERROR, "NCDValue_InitCopy failed");
+                return 0;
+            }
+            
+            return 1;
+        }
+        
         process_log(p, BLOG_ERROR, "unknown statement name in variable: %s.%s", modname, varname);
         return 0;
     }
@@ -1265,3 +1334,40 @@ NCDModuleInst * process_statement_instance_handler_getobj (struct process_statem
     
     return &rps->inst;
 }
+
+int process_statement_instance_handler_initprocess (struct process_statement *ps, NCDModuleProcess *mp, const char *template_name, NCDValue args)
+{
+    ASSERT(ps->state != SSTATE_FORGOTTEN)
+    ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST)
+    
+    // find template
+    struct NCDConfig_interfaces *conf = config_ast;
+    while (conf) {
+        if (conf->is_template && !strcmp(conf->name, template_name)) {
+            break;
+        }
+        conf = conf->next;
+    }
+    
+    if (!conf) {
+        process_statement_log(ps, BLOG_ERROR, "no template named %s", template_name);
+        return 0;
+    }
+    
+    // create process
+    if (!process_new(conf, mp, args)) {
+        process_statement_log(ps, BLOG_ERROR, "failed to create process from template %s", template_name);
+        return 0;
+    }
+    
+    process_statement_log(ps, BLOG_INFO, "created process from template %s", template_name);
+    
+    return 1;
+}
+
+void process_moduleprocess_handler_die (struct process *p)
+{
+    process_log(p, BLOG_INFO, "process termination requested");
+    
+    process_start_teminating(p);
+}

+ 2 - 1
ncdconfig/NCDConfig.c

@@ -90,7 +90,7 @@ void NCDConfig_free_strings (struct NCDConfig_strings *v)
     free(v);
 }
 
-struct NCDConfig_interfaces * NCDConfig_make_interfaces (char *name, struct NCDConfig_statements *statements, int need_next, struct NCDConfig_interfaces *next)
+struct NCDConfig_interfaces * NCDConfig_make_interfaces (int is_template, char *name, struct NCDConfig_statements *statements, int need_next, struct NCDConfig_interfaces *next)
 {
     if (!name || !statements || (need_next && !next)) {
         goto fail;
@@ -101,6 +101,7 @@ struct NCDConfig_interfaces * NCDConfig_make_interfaces (char *name, struct NCDC
         goto fail;
     }
     
+    v->is_template = is_template;
     v->name = name;
     v->statements = statements;
     v->next = next;

+ 2 - 1
ncdconfig/NCDConfig.h

@@ -29,6 +29,7 @@ struct NCDConfig_arguments;
 struct NCDConfig_strings;
 
 struct NCDConfig_interfaces {
+    int is_template;
     char *name;
     struct NCDConfig_statements *statements;
     struct NCDConfig_interfaces *next;
@@ -63,7 +64,7 @@ void NCDConfig_free_interfaces (struct NCDConfig_interfaces *v);
 void NCDConfig_free_statements (struct NCDConfig_statements *v);
 void NCDConfig_free_arguments (struct NCDConfig_arguments *v);
 void NCDConfig_free_strings (struct NCDConfig_strings *v);
-struct NCDConfig_interfaces * NCDConfig_make_interfaces (char *name, struct NCDConfig_statements *statements, int have_next, struct NCDConfig_interfaces *next);
+struct NCDConfig_interfaces * NCDConfig_make_interfaces (int is_template, char *name, struct NCDConfig_statements *statements, int have_next, struct NCDConfig_interfaces *next);
 struct NCDConfig_statements * NCDConfig_make_statements (struct NCDConfig_strings *objname, struct NCDConfig_strings *names, struct NCDConfig_arguments *args, char *name, struct NCDConfig_statements *next);
 struct NCDConfig_arguments * NCDConfig_make_arguments_string (char *str, struct NCDConfig_arguments *next);
 struct NCDConfig_arguments * NCDConfig_make_arguments_var (struct NCDConfig_strings *var, struct NCDConfig_arguments *next);

+ 4 - 0
ncdconfig/NCDConfigParser.c

@@ -92,6 +92,10 @@ static int tokenizer_output (void *user, int token, char *value, size_t position
             Parse(state->parser, PROCESS, NULL, &state->out);
         } break;
         
+        case NCD_TOKEN_TEMPLATE: {
+            Parse(state->parser, TEMPLATE, NULL, &state->out);
+        } break;
+        
         case NCD_TOKEN_NAME: {
             char *v = malloc(strlen(value) + 1);
             if (!v) {

+ 13 - 4
ncdconfig/NCDConfigParser_parse.y

@@ -48,6 +48,7 @@ struct parser_out {
 %type statement_args_maybe {struct NCDConfig_arguments *}
 %type statement_args {struct NCDConfig_arguments *}
 %type name_maybe {char *}
+%type process_or_template {int}
 
 %destructor interfaces { NCDConfig_free_interfaces($$); }
 %destructor statements { NCDConfig_free_statements($$); }
@@ -77,15 +78,15 @@ input ::= interfaces(A). {
     }
 }
 
-interfaces(R) ::= PROCESS NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
-    R = NCDConfig_make_interfaces(A, B, 0, NULL);
+interfaces(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
+    R = NCDConfig_make_interfaces(T, A, B, 0, NULL);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-interfaces(R) ::= PROCESS NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE interfaces(N). {
-    R = NCDConfig_make_interfaces(A, B, 1, N);
+interfaces(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE interfaces(N). {
+    R = NCDConfig_make_interfaces(T, A, B, 1, N);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
@@ -176,3 +177,11 @@ name_maybe(R) ::= . {
 name_maybe(R) ::= NAME(A). {
     R = A;
 }
+
+process_or_template(R) ::= PROCESS. {
+    R = 0;
+}
+
+process_or_template(R) ::= TEMPLATE. {
+    R = 1;
+}

+ 3 - 0
ncdconfig/NCDConfigTokenizer.c

@@ -112,6 +112,9 @@ void NCDConfigTokenizer_Tokenize (char *str, size_t left, NCDConfigTokenizer_out
             if (!strcmp(dec, "process")) {
                 token = NCD_TOKEN_PROCESS;
             }
+            else if (!strcmp(dec, "template")) {
+                token = NCD_TOKEN_TEMPLATE;
+            }
             else {
                 token = NCD_TOKEN_NAME;
                 token_val = dec;

+ 1 - 0
ncdconfig/NCDConfigTokenizer.h

@@ -36,6 +36,7 @@
 #define NCD_TOKEN_NAME 9
 #define NCD_TOKEN_STRING 10
 #define NCD_TOKEN_ARROW 11
+#define NCD_TOKEN_TEMPLATE 12
 
 #define NCD_MAX_SIZE 128