%{ /* MeTA1 configuration parser for GNU Pies. Copyright (C) 2008, 2009, 2010, 2011, 2013 Sergey Poznyakoff GNU Pies is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Pies 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 GNU Pies. If not, see . */ /* This file implements a grammar for parsing MeTA1 main configuration file, and a set of functions for converting its statements into Pies configuration statements. */ #include "pies.h" #include "grecs-locus.h" #include "meta1lex.h" #include "meta1gram.h" #define META1_QUEUE_DIR() \ (meta1_queue_dir ? meta1_queue_dir : "/var/spool/meta1") enum meta1_stmt_type { meta1_simple, meta1_block }; struct meta1_stmt { struct meta1_stmt *next; grecs_locus_t locus; enum meta1_stmt_type type; const char *ident; union { grecs_value_t *value; struct meta1_stmt *list; } v; }; struct meta1_stmt * meta1_stmt_create (enum meta1_stmt_type type, const char *ident) { struct meta1_stmt *p = xmalloc (sizeof (*p)); p->next = NULL; p->type = type; p->ident = ident; p->locus = meta1lloc; return p; } static struct meta1_stmt * _reverse (struct meta1_stmt *list, struct meta1_stmt **root) { struct meta1_stmt *next; if (list->next == NULL) { *root = list; return list; } next = _reverse (list->next, root); next->next = list; list->next = NULL; return list; } static struct meta1_stmt * reverse (struct meta1_stmt *in) { struct meta1_stmt *root; if (!in) return in; _reverse (in, &root); return root; } static void meta1_translate (struct meta1_stmt *); %} %error-verbose %locations %union { char *string; struct grecs_list *list; grecs_value_t *value; struct meta1_stmt *stmt; } %token META1_IDENT META1_STRING META1_NUMBER %type slist values list %type value %type string ident tag %type stmtlist stmt simple block %% input : stmtlist { struct meta1_stmt *stmt; for (stmt = $1; stmt; stmt = stmt->next) meta1_translate (stmt); } ; stmtlist: stmt { $$ = $1; } | stmtlist stmt { $2->next = $1; $$ = $2; } ; stmt : simple | block ; simple : ident '=' value opt_sc { $$ = meta1_stmt_create (meta1_simple, $1); $$->v.value = $3; } ; block : ident tag '{' stmtlist '}' opt_sc { $$ = meta1_stmt_create (meta1_block, $2 ? $2 : $1); $$->v.list = reverse ($4); } ; tag : /* empty */ { $$ = NULL; } | META1_IDENT ; ident : META1_IDENT ; value : string { $$ = xmalloc (sizeof (*$$)); $$->type = GRECS_TYPE_STRING; $$->v.string = $1; } | list { $$ = xmalloc (sizeof (*$$)); $$->type = GRECS_TYPE_LIST; $$->v.list = $1; } | META1_NUMBER { $$ = xmalloc (sizeof (*$$)); $$->type = GRECS_TYPE_STRING; $$->v.string = $1; } ; string : META1_IDENT | slist { struct grecs_list_entry *ep; meta1_line_begin (); for (ep = $1->head; ep; ep = ep->next) { const char *p = ep->data; meta1_line_add (p, strlen (p)); } $$ = meta1_line_finish (); } ; slist : META1_STRING { $$ = grecs_list_create (); grecs_list_append ($$, $1); } | slist META1_STRING { grecs_list_append ($1, $2); $$ = $1; } ; list : '{' values '}' { $$ = $2; } | '{' values ',' '}' { $$ = $2; } ; values : value { $$ = grecs_list_create (); grecs_list_append ($$, $1); } | values ',' value { grecs_list_append ($1, $3); $$ = $1; } ; opt_sc : /* empty */ | ';' ; %% int yyerror (char *s) { grecs_error (&yylloc, 0, "%s", s); return 0; } void meta1_parser_set_debug () { char *p = getenv ("META1_DEBUG_YACC"); yydebug = p && (*p - '0') > 0; } static struct meta1_stmt * find_stmt (struct meta1_stmt *stmt, const char *ident) { for (; stmt; stmt = stmt->next) if (strcmp (stmt->ident, ident) == 0) break; return stmt; } struct node_trans { char *name; char *new_name; int (*xlat) (struct meta1_stmt *stmt, struct component *comp); }; static struct node_trans * find_node_trans (struct node_trans *tab, const char *name) { for (; tab->name; tab++) if (strcmp (tab->name, name) == 0) return tab; return NULL; } static int xlat_listen_socket (struct meta1_stmt *stmt, struct component *comp) { struct meta1_stmt *p; grecs_value_t *val; p = find_stmt (stmt->v.list, "type"); if (!p || !p->v.value || p->v.value->type != GRECS_TYPE_STRING) return 1; meta1_line_begin (); meta1_line_add (p->v.value->v.string, strlen (p->v.value->v.string)); meta1_line_add ("://", 3); if (strcmp (p->v.value->v.string, "inet") == 0) { const char *addr; p = find_stmt (stmt->v.list, "address"); if (p) { if (p->v.value->type != GRECS_TYPE_STRING) return 1; addr = p->v.value->v.string; } else addr = "0.0.0.0"; meta1_line_add (addr, strlen (addr)); meta1_line_add (":", 1); p = find_stmt (stmt->v.list, "port"); if (p->v.value->type != GRECS_TYPE_STRING) return 1; meta1_line_add (p->v.value->v.string, strlen (p->v.value->v.string)); } else if (strcmp (p->v.value->v.string, "unix") == 0) { /* listen_socket { type=unix; path = /tmp/socket; umask = 077; user = user; group = group; } */ p = find_stmt (stmt->v.list, "path"); if (!p || p->v.value->type != GRECS_TYPE_STRING) return 1; meta1_line_add (p->v.value->v.string, strlen (p->v.value->v.string)); p = find_stmt (stmt->v.list, "user"); if (p && p->v.value->type == GRECS_TYPE_STRING) { meta1_line_add (";user=", 6); meta1_line_add (p->v.value->v.string, strlen (p->v.value->v.string)); } p = find_stmt (stmt->v.list, "group"); if (p && p->v.value->type == GRECS_TYPE_STRING) { meta1_line_add (";group=", 7); meta1_line_add (p->v.value->v.string, strlen (p->v.value->v.string)); } p = find_stmt (stmt->v.list, "umask"); if (p && p->v.value->type == GRECS_TYPE_STRING) { meta1_line_add (";umask=", 7); meta1_line_add (p->v.value->v.string, strlen (p->v.value->v.string)); } } val = xmalloc (sizeof (*val)); val->type = GRECS_TYPE_STRING; val->v.string = meta1_line_finish (); stmt->type = meta1_simple; stmt->v.value = val; return 0; } static struct node_trans root_node_trans[] = { { "listen_socket", "socket", xlat_listen_socket }, { "start_action", "mode" }, { "pass_fd_socket", "pass-fd-socket" }, { "user", "user" }, { "path", "program" }, { "arguments", "command" }, { "restart_dependencies", "dependents", }, { NULL } }; static void meta1_translate_stmt (struct meta1_stmt *stmt, struct component *comp) { struct grecs_keyword *kwp; struct node_trans *nt = find_node_trans (root_node_trans, stmt->ident); if (!nt) return; kwp = find_component_keyword (nt->new_name); if (!kwp) abort (); if (nt->xlat && nt->xlat (stmt, comp)) return; grecs_process_ident (kwp, stmt->v.value, comp, &stmt->locus); } static void meta1_translate (struct meta1_stmt *stmt) { struct component *comp; struct meta1_stmt *p; if (stmt->type != meta1_block) return; comp = component_create (stmt->ident); for (p = stmt->v.list; p; p = p->next) { meta1_translate_stmt (p, comp); } comp->privs.allgroups = 1; comp->dir = META1_QUEUE_DIR (); comp->redir[RETR_ERR].type = redir_file; comp->redir[RETR_ERR].v.file = xasprintf ("%s/%s.log", META1_QUEUE_DIR (), comp->tag); component_finish (comp, &stmt->locus); }