aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore6
-rw-r--r--src/Makefile.am57
-rw-r--r--src/depmap.c170
-rw-r--r--src/limits.c299
-rw-r--r--src/meta1gram.y439
-rw-r--r--src/meta1lex.h31
-rw-r--r--src/meta1lex.l231
-rw-r--r--src/pies.c1581
-rw-r--r--src/pies.h205
-rw-r--r--src/pies.rcin31
-rw-r--r--src/progman.c1741
-rw-r--r--src/socket.c446
12 files changed, 5237 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..8a4684a
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,6 @@
+meta1gram.c
+meta1gram.h
+meta1gram.output
+meta1lex.c
+pies
+pies.rc
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..52648c8
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,57 @@
+# This file is part of mailfrom filter.
+# Copyright (C) 2008, 2009 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 <http://www.gnu.org/licenses/>.
+
+sbin_PROGRAMS = pies
+
+pies_SOURCES = \
+ depmap.c\
+ limits.c\
+ meta1gram.y\
+ meta1lex.l\
+ pies.c\
+ progman.c\
+ socket.c
+
+noinst_HEADERS = \
+ meta1gram.h\
+ meta1lex.h\
+ pies.h
+
+EXTRA_DIST = pies.rcin
+noinst_DATA = pies.rc
+DISTCLEANFILES = pies.rc
+.rcin.rc:
+ $(AM_V_GEN)sed 's|SBINDIR|$(sbindir)|g' $< > $@
+
+meta1lex.c: meta1gram.h
+
+INCLUDES = \
+ $(MAILUTILS_INCLUDES)\
+ $(MU_COMMON_INCLUDES)\
+ -I$(top_srcdir)/lib\
+ -I$(top_srcdir)/gnu
+
+LDADD = \
+ ../lib/libmf.a\
+ ../gnu/libgnu.a\
+ $(MAILUTILS_LIBS)\
+ $(MF_PROCTITLE_LIBS)
+
+AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\"\
+ -DSTATEDIR=\"$(localstatedir)\"
+
+AM_YFLAGS=-dvt -pmeta1
+AM_LFLAGS=-dvp -Pmeta1 -olex.yy.c
diff --git a/src/depmap.c b/src/depmap.c
new file mode 100644
index 0000000..76b492f
--- /dev/null
+++ b/src/depmap.c
@@ -0,0 +1,170 @@
+/* This file is part of 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 of the License, 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 <http://www.gnu.org/licenses/>. */
+
+#include "pies.h"
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+#define BITS_PER_WORD (sizeof(unsigned)*CHAR_BIT)
+#define MAXTABLE 32767
+
+#define WORDSIZE(n) (((n) + BITS_PER_WORD - 1) / BITS_PER_WORD)
+#define SETBIT(x, i) ((x)[(i)/BITS_PER_WORD] |= (1<<((i) % BITS_PER_WORD)))
+#define RESETBIT(x, i) ((x)[(i)/BITS_PER_WORD] &= ~(1<<((i) % BITS_PER_WORD)))
+#define BITISSET(x, i) (((x)[(i)/BITS_PER_WORD] & (1<<((i) % BITS_PER_WORD))) != 0)
+
+void
+TC (unsigned *R, int n)
+{
+ register int rowsize;
+ register unsigned mask;
+ register unsigned *rowj;
+ register unsigned *rp;
+ register unsigned *rend;
+ register unsigned *ccol;
+
+ unsigned *relend;
+ unsigned *cword;
+ unsigned *rowi;
+
+ rowsize = WORDSIZE (n) * sizeof (unsigned);
+ relend = (unsigned *) ((char *) R + (n * rowsize));
+
+ cword = R;
+ mask = 1;
+ rowi = R;
+ while (rowi < relend)
+ {
+ ccol = cword;
+ rowj = R;
+
+ while (rowj < relend)
+ {
+ if (*ccol & mask)
+ {
+ rp = rowi;
+ rend = (unsigned *) ((char *) rowj + rowsize);
+
+ while (rowj < rend)
+ *rowj++ |= *rp++;
+ }
+ else
+ {
+ rowj = (unsigned *) ((char *) rowj + rowsize);
+ }
+
+ ccol = (unsigned *) ((char *) ccol + rowsize);
+ }
+
+ mask <<= 1;
+ if (mask == 0)
+ {
+ mask = 1;
+ cword++;
+ }
+ rowi = (unsigned *) ((char *) rowi + rowsize);
+ }
+}
+
+struct pies_depmap
+{
+ unsigned nrows;
+ unsigned rowlen;
+ unsigned r[1];
+};
+
+pies_depmap_t
+depmap_alloc (unsigned count)
+{
+ size_t size = (count + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ pies_depmap_t dmap = xzalloc (sizeof (*dmap) - 1
+ + count * size * sizeof (unsigned));
+ dmap->nrows = count;
+ dmap->rowlen = size;
+ return dmap;
+}
+
+pies_depmap_t
+depmap_copy (pies_depmap_t dpm)
+{
+ pies_depmap_t copy = depmap_alloc (dpm->nrows);
+ memcpy (copy->r, dpm->r, dpm->nrows * dpm->rowlen * sizeof (unsigned));
+ return copy;
+}
+
+static unsigned *
+depmap_rowptr (pies_depmap_t dmap, unsigned row)
+{
+ return dmap->r + dmap->rowlen * row;
+}
+
+void
+depmap_set (pies_depmap_t dmap, unsigned row, unsigned col)
+{
+ unsigned *rptr = depmap_rowptr (dmap, row);
+ SETBIT (rptr, col);
+}
+
+int
+depmap_isset (pies_depmap_t dmap, unsigned row, unsigned col)
+{
+ unsigned *rptr = depmap_rowptr (dmap, row);
+ return BITISSET (rptr, col);
+}
+
+void
+depmap_tc (pies_depmap_t dmap)
+{
+ TC (dmap->r, dmap->nrows);
+}
+
+struct pies_depmap_pos
+{
+ enum pies_depmap_direction dir;
+ unsigned coord[2];
+};
+
+unsigned
+depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos)
+{
+ for (pos->coord[pos->dir]++; pos->coord[pos->dir] < dmap->nrows;
+ pos->coord[pos->dir]++)
+ if (depmap_isset (dmap, pos->coord[0], pos->coord[1]))
+ break;
+
+ return pos->coord[pos->dir] == dmap->nrows ?
+ (unsigned) -1 : pos->coord[pos->dir];
+}
+
+unsigned
+depmap_first (pies_depmap_t dmap, enum pies_depmap_direction dir,
+ unsigned coord, pies_depmap_pos_t *ppos)
+{
+ pies_depmap_pos_t pos = xmalloc (sizeof *pos);
+ *ppos = pos;
+ pos->dir = dir;
+ pos->coord[!pos->dir] = coord;
+ pos->coord[pos->dir] = -1;
+ return depmap_next (dmap, pos);
+}
+
+/*
+ Local Variables:
+ c-file-style: "gnu"
+ End:
+*/
+/* EOF */
diff --git a/src/limits.c b/src/limits.c
new file mode 100644
index 0000000..90d6459
--- /dev/null
+++ b/src/limits.c
@@ -0,0 +1,299 @@
+/* This file is part of 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 <http://www.gnu.org/licenses/>. */
+
+#include <pies.h>
+
+#define SET_LIMIT_AS 0x0001
+#define SET_LIMIT_CPU 0x0002
+#define SET_LIMIT_DATA 0x0004
+#define SET_LIMIT_FSIZE 0x0008
+#define SET_LIMIT_NPROC 0x0010
+#define SET_LIMIT_CORE 0x0020
+#define SET_LIMIT_MEMLOCK 0x0040
+#define SET_LIMIT_NOFILE 0x0080
+#define SET_LIMIT_RSS 0x0100
+#define SET_LIMIT_STACK 0x0200
+#define SET_LIMIT_LOGINS 0x0400
+#define SET_LIMIT_PRIO 0x0800
+
+struct limits_rec {
+ unsigned set;
+ rlim_t limit_as;
+ rlim_t limit_cpu;
+ rlim_t limit_data;
+ rlim_t limit_fsize;
+ rlim_t limit_nproc;
+ rlim_t limit_core;
+ rlim_t limit_memlock;
+ rlim_t limit_nofile;
+ rlim_t limit_rss;
+ rlim_t limit_stack;
+ int limit_logins;
+ int limit_prio;
+};
+
+int
+do_set_limit (int rlimit, rlim_t limit)
+{
+ struct rlimit rlim;
+
+ MU_DEBUG2 (pies_debug, MU_DEBUG_TRACE1,
+ "Setting limit %d to %lu\n", rlimit, (unsigned long) limit);
+ rlim.rlim_cur = limit;
+ rlim.rlim_max = limit;
+
+ if (setrlimit(rlimit, &rlim))
+ {
+ mu_diag_output (MU_DIAG_NOTICE, _("error setting limit: %s"),
+ mu_strerror (errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+set_prio (int prio)
+{
+ MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE2, "Setting priority to %d\n", prio);
+ if (setpriority (PRIO_PROCESS, 0, prio))
+ {
+ mu_diag_output (MU_DIAG_NOTICE, _("error setting priority: %s"),
+ mu_strerror (errno));
+ return 1;
+ }
+ return 0;
+}
+
+/* Counts the number of user logins and check against the limit */
+static int
+check_logins (const char *name, int limit)
+{
+ mu_diag_output (MU_DIAG_NOTICE, _("L limit is not implemented"));
+ return 0;
+}
+
+int
+set_limits (const char *name, struct limits_rec *lrec)
+{
+ int rc = 0;
+
+ if (!lrec)
+ return 0;
+
+ MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE2, "Setting limits for %s\n", name);
+
+#if defined(RLIMIT_AS)
+ if (lrec->set & SET_LIMIT_AS)
+ rc |= do_set_limit(RLIMIT_AS, lrec->limit_as);
+#endif
+#if defined(RLIMIT_CPU)
+ if (lrec->set & SET_LIMIT_CPU)
+ rc |= do_set_limit(RLIMIT_CPU, lrec->limit_cpu);
+#endif
+#if defined(RLIMIT_DATA)
+ if (lrec->set & SET_LIMIT_DATA)
+ rc |= do_set_limit(RLIMIT_DATA, lrec->limit_data);
+#endif
+#if defined(RLIMIT_FSIZE)
+ if (lrec->set & SET_LIMIT_FSIZE)
+ rc |= do_set_limit(RLIMIT_FSIZE, lrec->limit_fsize);
+#endif
+#if defined(RLIMIT_NPROC)
+ if (lrec->set & SET_LIMIT_NPROC)
+ rc |= do_set_limit(RLIMIT_NPROC, lrec->limit_nproc);
+#endif
+#if defined(RLIMIT_CORE)
+ if (lrec->set & SET_LIMIT_CORE)
+ rc |= do_set_limit(RLIMIT_CORE, lrec->limit_core);
+#endif
+#if defined(RLIMIT_MEMLOCK)
+ if (lrec->set & SET_LIMIT_MEMLOCK)
+ rc |= do_set_limit(RLIMIT_MEMLOCK, lrec->limit_memlock);
+#endif
+#if defined(RLIMIT_NOFILE)
+ if (lrec->set & SET_LIMIT_NOFILE)
+ rc |= do_set_limit(RLIMIT_NOFILE, lrec->limit_nofile);
+#endif
+#if defined(RLIMIT_RSS)
+ if (lrec->set & SET_LIMIT_RSS)
+ rc |= do_set_limit(RLIMIT_RSS, lrec->limit_rss);
+#endif
+#if defined(RLIMIT_STACK)
+ if (lrec->set & SET_LIMIT_STACK)
+ rc |= do_set_limit(RLIMIT_STACK, lrec->limit_stack);
+#endif
+ if (lrec->set & SET_LIMIT_LOGINS)
+ rc |= check_logins(name, lrec->limit_logins);
+ if (lrec->set & SET_LIMIT_PRIO)
+ rc |= set_prio(lrec->limit_logins);
+ return rc;
+}
+
+int
+getlimit (char **ptr, rlim_t *rlim, int mul)
+{
+ if (**ptr == '-')
+ {
+ *rlim = RLIM_INFINITY;
+ ++*ptr;
+ }
+ else
+ {
+ unsigned long val;
+
+ val = strtoul (*ptr, ptr, 10);
+ if (val == 0)
+ return 1;
+ *rlim = val * mul;
+ }
+ return 0;
+}
+
+/* Parse limits string and fill appropriate fields in lrec.
+
+ The string consists of _commands_, optionally separated by any amount
+ of whitespace. A command has the following form:
+
+ [AaCcDdFfMmNnRrSsTtUuLlPp](-|[0-9]+)
+
+ i.e. a letter followed by number or a dash. The latters stands for
+ 'unlimited'. Commands are interpreted as follows:
+
+ Command ulimit setrlimit() The limit it sets
+ option arg
+ -------------------------------------------------------------
+ [Aa] a RLIMIT_AS max address space (KB)
+ [Cc] c RLIMIT_CORE max core file size (KB)
+ [Dd] d RLIMIT_DATA max data size (KB)
+ [Ff] f RLIMIT_FSIZE Maximum filesize (KB)
+ [Mm] m RLIMIT_MEMLOCK max locked-in-memory address
+ space (KB)
+ [Nn] n RLIMIT_NOFILE max number of open files
+ [Rr] r RLIMIT_RSS max resident set size (KB)
+ [Ss] s RLIMIT_STACK max stack size (KB)
+ [Tt] t RLIMIT_CPU max CPU time (MIN)
+ [Uu] u RLIMIT_NPROC max number of processes
+ [Ll] l (none) max number of logins (N/A)
+ [Pp] p (none) process priority -20..20
+ (negative = high priority)
+ */
+int
+parse_limits (limits_record_t *plrec, char *str, char **endp)
+{
+ int c;
+ struct limits_rec *lrec = xmalloc (sizeof (*lrec));
+ *plrec = lrec;
+ lrec->set = 0;
+ while ((c = *str++))
+ {
+ if (c == ' ' || c == '\t')
+ continue;
+ switch (c)
+ {
+ case 'a':
+ case 'A':
+ /* RLIMIT_AS - max address space (KB) */
+ if (!getlimit (&str, &lrec->limit_as, 1024))
+ lrec->set |= SET_LIMIT_AS;
+ break;
+
+ case 't':
+ case 'T':
+ /* RLIMIT_CPU - max CPU time (MIN) */
+ if (!getlimit (&str, &lrec->limit_cpu, 60))
+ lrec->set |= SET_LIMIT_CPU;
+ break;
+
+ case 'd':
+ case 'D':
+ /* RLIMIT_DATA - max data size (KB) */
+ if (!getlimit (&str, &lrec->limit_data, 1024))
+ lrec->set |= SET_LIMIT_DATA;
+ break;
+
+ case 'f':
+ case 'F':
+ /* RLIMIT_FSIZE - Maximum filesize (KB) */
+ if (!getlimit (&str, &lrec->limit_fsize, 1024))
+ lrec->set |= SET_LIMIT_FSIZE;
+ break;
+
+ case 'u':
+ case 'U':
+ /* RLIMIT_NPROC - max number of processes */
+ if (!getlimit (&str, &lrec->limit_nproc, 1))
+ lrec->set |= SET_LIMIT_NPROC;
+ break;
+
+ case 'c':
+ case 'C':
+ /* RLIMIT_CORE - max core file size (KB) */
+ if (!getlimit (&str, &lrec->limit_core, 1024))
+ lrec->set |= SET_LIMIT_CORE;
+ break;
+
+ case 'm':
+ case 'M':
+ /* RLIMIT_MEMLOCK - max locked-in-memory
+ * address space (KB)
+ */
+ if (!getlimit (&str, &lrec->limit_memlock, 1024))
+ lrec->set |= SET_LIMIT_MEMLOCK;
+ break;
+
+ case 'n':
+ case 'N':
+ /* RLIMIT_NOFILE - max number of open files */
+ if (!getlimit (&str, &lrec->limit_nofile, 1))
+ lrec->set |= SET_LIMIT_NOFILE;
+ break;
+
+ case 'r':
+ case 'R':
+ /* RLIMIT_RSS - max resident set size (KB) */
+ if (!getlimit (&str, &lrec->limit_rss, 1024))
+ lrec->set |= SET_LIMIT_RSS;
+ break;
+
+ case 's':
+ case 'S':
+ /* RLIMIT_STACK - max stack size (KB) */
+ if (!getlimit (&str, &lrec->limit_stack, 1024))
+ lrec->set |= SET_LIMIT_STACK;
+ break;
+
+ case 'l':
+ case 'L':
+ lrec->limit_logins = strtol (str, &str, 10);
+ if (lrec->limit_logins >= 0)
+ lrec->set |= SET_LIMIT_LOGINS;
+ break;
+
+ case 'p':
+ case 'P':
+ lrec->limit_prio = strtol (str, &str, 10);
+ if (lrec->limit_prio > 0)
+ lrec->set |= SET_LIMIT_PRIO;
+ break;
+
+ default:
+ *endp = str-1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
diff --git a/src/meta1gram.y b/src/meta1gram.y
new file mode 100644
index 0000000..4966634
--- /dev/null
+++ b/src/meta1gram.y
@@ -0,0 +1,439 @@
+%{
+/* 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 <http://www.gnu.org/licenses/>. */
+
+/* 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 <string> META1_IDENT META1_STRING META1_NUMBER
+
+%type <nodelist> stmtlist
+%type <pnode> stmt simple block
+%type <list> slist list values
+%type <string> string ident
+%type <value> 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;
+}
+
diff --git a/src/meta1lex.h b/src/meta1lex.h
new file mode 100644
index 0000000..8db32f9
--- /dev/null
+++ b/src/meta1lex.h
@@ -0,0 +1,31 @@
+/* This file is part of 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 <http://www.gnu.org/licenses/>. */
+
+extern mu_cfg_locus_t meta1_locus;
+extern size_t meta1_error_count;
+extern mu_cfg_node_t *meta1_parse_head;
+extern mu_cfg_tree_t *meta1_parse_tree;
+extern char *meta1_queue_dir;
+
+char *meta1_string (const char *str, size_t len);
+void meta1_line_add (const char *text, size_t len);
+void meta1_line_add_unescape_last (const char *text, size_t len);
+void meta1_line_add_unescape_hex (const char *text, size_t len);
+void meta1_line_begin (void);
+char *meta1_line_finish ();
+void meta1_parse_error (const char *fmt, ...);
+int meta1_config_parse (const char *name);
+
diff --git a/src/meta1lex.l b/src/meta1lex.l
new file mode 100644
index 0000000..312a1fd
--- /dev/null
+++ b/src/meta1lex.l
@@ -0,0 +1,231 @@
+%{
+/* MeTA1 configuration lexer 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 <http://www.gnu.org/licenses/>. */
+
+/* This file implements a lexical analyzer for MeTA1 main configuration file.
+ */
+
+#include "pies.h"
+#include "meta1gram.h"
+#include "meta1lex.h"
+
+mu_cfg_locus_t meta1_locus;
+size_t meta1_error_count;
+mu_opool_t meta1_pool;
+char *meta1_queue_dir;
+
+#define yylval meta1lval
+%}
+
+%x COMMENT STR
+X [0-9a-fA-F]
+%%
+ /* C-style comments */
+"/*" BEGIN (COMMENT);
+<COMMENT>[^*\n]* /* eat anything that's not a '*' */
+<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<COMMENT>\n ++meta1_locus.line;
+<COMMENT>"*"+"/" BEGIN (INITIAL);
+ /* End-of-line comments */
+#.*\n { meta1_locus.line++; }
+#.* /* end-of-file comment */;
+ /* Number */
+0[xX]{X}+ |
+0[0-7]+ |
+[1-9][0-9]+ { meta1_line_begin ();
+ meta1_line_add (yytext, yyleng);
+ yylval.string = meta1_line_finish ();
+ return META1_NUMBER; }
+ /* Identifiers (unquoted strings) */
+[a-zA-Z0-9_\./:\*-]+ { meta1_line_begin ();
+ meta1_line_add (yytext, yyleng);
+ yylval.string = meta1_line_finish ();
+ return META1_IDENT; }
+ /* Quoted strings */
+\"[^\\"\n]*\" { meta1_line_begin ();
+ meta1_line_add (yytext + 1, yyleng - 2);
+ yylval.string = meta1_line_finish ();
+ return META1_STRING; }
+\"[^\\"\n]*\\x{X}{1,2} { BEGIN(STR);
+ meta1_line_begin ();
+ meta1_line_add_unescape_hex (yytext + 1, yyleng - 1);
+ }
+\"[^\\"\n]*\\. { BEGIN(STR);
+ meta1_line_begin ();
+ meta1_line_add_unescape_last (yytext + 1, yyleng - 1); }
+<STR>[^\\"\n]*\\x{X}{1,2} { meta1_line_add_unescape_hex (yytext, yyleng); }
+<STR>[^\\"\n]*\\. { meta1_line_add_unescape_last (yytext, yyleng); }
+<STR>[^\\"\n]*\" { BEGIN (INITIAL);
+ if (yyleng > 1)
+ meta1_line_add (yytext, yyleng - 1);
+ yylval.string = meta1_line_finish ();
+ return META1_STRING; }
+<STR>[^\\"\n]*\n { BEGIN (INITIAL);
+ meta1_parse_error (_("newline in a string"));
+ meta1_line_add (yytext, yyleng - 1);
+ yylval.string = meta1_line_finish ();
+ return META1_STRING; }
+ /* Other tokens */
+[ \t\f][ \t\f]*