%{
/* MeTA1 configuration parser for Mailfromd.
Copyright (C) 2008 Sergey Poznyakoff
This program 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.
This program 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, 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 "meta1lex.h"
/* FIXME: Use mu_opool_alloc? */
static mu_config_value_t *
create_value (int type)
{
mu_config_value_t *val = mu_alloc (sizeof (*val));
val->type = type;
return val;
}
static mu_config_value_t *
config_value_dup (mu_config_value_t *src)
{
if (!src)
return NULL;
else
{
mu_config_value_t *val = mu_alloc (sizeof (*val));
*val = *src;
return val;
}
}
static mu_cfg_node_t *
alloc_node (enum mu_cfg_node_type type, mu_cfg_locus_t *loc,
const char *tag, mu_config_value_t *label,
mu_cfg_node_t *node)
{
char *p;
mu_cfg_node_t *np;
size_t size = sizeof *np + strlen (tag) + 1;
np = mu_alloc (size);
np->type = type;
np->locus = *loc;
p = (char*) (np + 1);
np->tag = p;
strcpy (p, tag);
np->label = label;
np->node = node;
np->next = NULL;
return np;
}
static mu_cfg_node_t *translate_component (mu_cfg_node_t *);
mu_cfg_node_t *meta1_parse_head;
%}
%union {
mu_cfg_node_t node;
mu_cfg_node_t *pnode;
mu_config_value_t *value;
struct { mu_cfg_node_t *head, *tail; } nodelist;
char *string;
unsigned long number;
mu_list_t list;
}
%token META1_IDENT META1_STRING META1_NUMBER
%type stmtlist
%type stmt simple block
%type slist list values
%type string ident
%type value
%%
input : stmtlist
{
mu_cfg_node_t *node;
meta1_parse_head = NULL;
for (node = $1.head; node; node = node->next)
{
mu_cfg_node_t *new_node = translate_component (node);
if (new_node)
{
new_node->next = meta1_parse_head;
meta1_parse_head = new_node;
}
}
}
;
stmtlist: stmt
{
$$.head = $$.tail = $1;
}
| stmtlist stmt
{
$$ = $1;
$$.tail->next = $2;
$$.tail = $2;
}
;
stmt : simple
| block
;
simple : ident '=' value opt_sc
{
$$ = alloc_node (mu_cfg_node_param, &meta1_locus,
$1, $3,
NULL);
}
;
block : ident tag '{' stmtlist '}' opt_sc
{
/* FIXME: tag is currently ignored */
$$ = alloc_node (mu_cfg_node_tag, &meta1_locus,
$1, NULL,
$4.head);
}
;
tag : /* empty */
| META1_IDENT
;
ident : META1_IDENT
;
value : string
{
$$ = create_value (MU_CFG_STRING);
$$->v.string = $1;
}
| list
{
$$ = create_value (MU_CFG_LIST);
$$->v.list = $1;
}
| META1_NUMBER
{
$$ = create_value (MU_CFG_STRING);
$$->v.string = $1;
}
;
string : META1_IDENT
| slist
{
mu_iterator_t itr;
mu_list_get_iterator ($1, &itr);
meta1_line_begin ();
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
{
char *p;
mu_iterator_current (itr, (void**)&p);
meta1_line_add (p, strlen (p));
}
$$ = meta1_line_finish ();
mu_iterator_destroy (&itr);
mu_list_destroy (&$1);
}
;
slist : META1_STRING
{
mu_list_create (&$$);
mu_list_append ($$, $1);
}
| slist META1_STRING
{
mu_list_append ($1, $2);
$$ = $1;
}
;
list : '{' values '}'
{
$$ = $2;
}
| '{' values ',' '}'
{
$$ = $2;
}
;
values : value
{
mu_list_create (&$$);
mu_list_append ($$, $1);
}
| values ',' value
{
mu_list_append ($1, $3);
$$ = $1;
}
;
opt_sc : /* empty */
| ';'
;
%%
int
yyerror (char *s)
{
meta1_parse_error ("%s", s);
return 0;
}
void
meta1_parser_set_debug ()
{
mu_log_level_t lev = mu_global_debug_level ("meta1");
if (lev & MU_DEBUG_LEVEL_MASK (MU_DEBUG_TRACE6))
yydebug = 1;
}
struct node_trans
{
char *name;
char *new_name;
mu_cfg_node_t *(*fun) (const char *, mu_cfg_node_t *);
};
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 mu_cfg_node_t *
find_node (mu_cfg_node_t *list, const char *name)
{
for (; list; list = list->next)
{
if (strcmp (list->tag, name) == 0)
return list;
}
return NULL;
}
static mu_cfg_node_t *
xlat_listen_socket (const char *name, mu_cfg_node_t *src)
{
mu_cfg_node_t *p;
mu_config_value_t *val;
meta1_line_begin ();
p = find_node (src->node, "type");
if (!p || !p->label || p->label->type != MU_CFG_STRING)
return NULL;
meta1_line_add (p->label->v.string, strlen (p->label->v.string));
meta1_line_add ("://", 3);
if (strcmp (p->label->v.string, "inet") == 0)
{
const char *addr;
p = find_node (src->node, "address");
if (p)
{
if (p->label->type != MU_CFG_STRING)
return NULL;
addr = p->label->v.string;
}
else
addr = "0.0.0.0";
meta1_line_add (addr, strlen (addr));
meta1_line_add (":", 1);
p = find_node (src->node, "port");
if (p->label->type != MU_CFG_STRING)
return NULL;
meta1_line_add (p->label->v.string, strlen (p->label->v.string));
}
else if (strcmp (p->label->v.string, "unix") == 0)
{
p = find_node (src->node, "path");
if (!p || p->label->type != MU_CFG_STRING)
return NULL;
meta1_line_add (p->label->v.string, strlen (p->label->v.string));
/* FIXME: Other substatements:
listen_socket {
type=unix;
path = /tmp/socket;
umask = 077;
user = user;
group = group;
}
*/
}
val = create_value (MU_CFG_STRING);
val->v.string = meta1_line_finish ();
return alloc_node (mu_cfg_node_param, &src->locus, "socket", val, NULL);
}
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 mu_cfg_node_t *
create_string_node (const char *tag, const char *str, mu_cfg_locus_t *locus)
{
mu_config_value_t *val = create_value (MU_CFG_STRING);
val->v.string = meta1_string (str, strlen (str));
return alloc_node (mu_cfg_node_param, locus, tag, val, NULL);
}
static mu_cfg_node_t *
create_redir_node (const char *tag, const char *dir,
const char *name, mu_cfg_locus_t *locus)
{
mu_config_value_t *val = create_value (MU_CFG_ARRAY);
val->v.arg.c = 2;
val->v.arg.v = mu_alloc (2 * sizeof (val->v.arg.v[0]));
val->v.arg.v[1].type = MU_CFG_STRING;
val->v.arg.v[0].v.string = meta1_string ("file", 4);
val->v.arg.v[1].type = MU_CFG_STRING;
meta1_line_begin ();
meta1_line_add (dir, strlen (dir));
meta1_line_add ("/", 1);
meta1_line_add (name, strlen (name));
meta1_line_add (".log", 4);
val->v.arg.v[1].v.string = meta1_line_finish ();
return alloc_node (mu_cfg_node_param, locus, tag, val, NULL);
}
static mu_cfg_node_t *
translate_node (mu_cfg_node_t *src)
{
mu_cfg_node_t *dst = NULL;
struct node_trans *nt = find_node_trans (root_node_trans, src->tag);
if (nt)
{
if (nt->fun)
dst = nt->fun (nt->new_name, src);
else
dst = alloc_node (mu_cfg_node_param, &src->locus,
nt->new_name, config_value_dup (src->label),
NULL);
}
return dst;
}
#define META1_QUEUE_DIR() \
(meta1_queue_dir ? meta1_queue_dir : "/var/spool/meta1")
static mu_cfg_node_t *
translate_node_list (mu_cfg_node_t *src, const char *name)
{
mu_cfg_node_t *head = NULL, *tail = NULL;
mu_cfg_locus_t *locus = &src->locus;
for (; src; src = src->next)
{
mu_cfg_node_t *dst = translate_node (src);
if (dst)
{
if (tail)
tail->next = dst;
else
head = dst;
tail = dst;
}
}
if (head)
{
/* Add common statements: */
mu_cfg_node_t *node;
node = create_string_node ("allgroups", "yes", locus);
tail->next = node;
tail = node;
node = create_string_node ("chdir", META1_QUEUE_DIR (), locus);
tail->next = node;
tail = node;
node = create_redir_node ("stderr", META1_QUEUE_DIR (), name, locus);
tail->next = node;
tail = node;
node = create_string_node ("settle-timeout", "1", locus);
tail->next = node;
tail = node;
}
return head;
}
static mu_cfg_node_t *
translate_component (mu_cfg_node_t *src)
{
mu_cfg_node_t *dst = NULL;
if (src->type == mu_cfg_node_tag)
{
mu_config_value_t *val = create_value (MU_CFG_STRING);
val->v.string = meta1_string (src->tag, strlen (src->tag));
dst = alloc_node (mu_cfg_node_tag, &src->locus,
"component", val,
translate_node_list (src->node, val->v.string));
}
return dst;
}