%{
/* 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 const *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);
}