/* This file is part of GNU Pies. Copyright (C) 2016 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 . */ #include "pies.h" char *meta1_queue_dir; #define META1_QUEUE_DIR() \ (meta1_queue_dir ? meta1_queue_dir : "/var/spool/meta1") static void setflag (int *flag, char const *var, int bit) { char *p = getenv (var); if (p && (*p - '0') > 0) *flag |= bit; } static enum grecs_tree_recurse_res freeproc (enum grecs_tree_recurse_op op, struct grecs_node *node, void *data) { switch (op) { case grecs_tree_recurse_set: case grecs_tree_recurse_post: grecs_node_unlink (node); grecs_node_free (node); break; case grecs_tree_recurse_pre: /* descend into the subtree */ break; } return grecs_tree_recurse_ok; } int grecs_subtree_free (struct grecs_node *node) { if (!node) return 0; if (node->type != grecs_node_block) { errno = EINVAL; return 1; } grecs_tree_recurse (node, freeproc, node); return 0; } static int xlat_start_action (struct grecs_node *node) { if (node->type != grecs_node_stmt) return -1; if (strcmp (node->v.value->v.string, "wait") == 0) { node->v.value->v.string = grecs_strdup ("exec"); } return 0; } enum substatement_status { substatement_success, substatement_error, substatement_not_found }; static struct grecs_node * simple_find_node (struct grecs_node *node, char const *name) { for (node = node->down; node; node = node->next) if (strcmp (node->ident, name) == 0) break; return node; } static enum substatement_status substatement_find (struct grecs_node *node, char const *name, int type, int req, grecs_value_t **ret) { struct grecs_node *np; np = simple_find_node (node, name); if (!np) { if (req) grecs_error (&node->locus, 0, _("no \"%s\" statement in block"), name); return substatement_not_found; } if (node->type != grecs_node_block) { grecs_error (&node->locus, 0, _("unexpected scalar statement")); return substatement_error; } if (assert_grecs_value_type (&np->v.value->locus, np->v.value, type)) return substatement_error; *ret = np->v.value; return substatement_success; } static int translate_url_param (struct grecs_node *node, char const *name, struct grecs_txtacc *acc) { struct grecs_value *np; switch (substatement_find (node, name, GRECS_TYPE_STRING, 0, &np)) { case substatement_success: grecs_txtacc_grow_char (acc, ';'); grecs_txtacc_grow_string (acc, name + 1); grecs_txtacc_grow_char (acc, '='); grecs_txtacc_grow_string (acc, np->v.string); break; case substatement_not_found: break; case substatement_error: return -1; } return 0; } static int xlat_listen_socket_i (struct grecs_node *node, char const *type, struct grecs_txtacc *acc) { struct grecs_value *val, *vp; char *url; grecs_txtacc_grow_string (acc, type); grecs_txtacc_grow (acc, "://", 3); if (strcmp (type, "inet") == 0) { const char *addr; switch (substatement_find (node, "address", GRECS_TYPE_STRING, 0, &vp)) { case substatement_success: addr = vp->v.string; break; case substatement_not_found: addr = "0.0.0.0"; break; case substatement_error: return -1; } grecs_txtacc_grow_string (acc, addr); grecs_txtacc_grow_char (acc, ':'); if (substatement_find (node, "port", GRECS_TYPE_STRING, 1, &vp) != substatement_success) return -1; grecs_txtacc_grow_string (acc, vp->v.string); } else if (strcmp (type, "unix") == 0) { /* listen_socket { type=unix; path = /tmp/socket; umask = 077; user = user; group = group; } */ if (substatement_find (node, "path", GRECS_TYPE_STRING, 1, &vp) != substatement_success) return -1; grecs_txtacc_grow_string (acc, vp->v.string); if (translate_url_param (node, "user", acc)) return -1; if (translate_url_param (node, "group", acc)) return -1; if (translate_url_param (node, "umask", acc)) return -1; } else { grecs_error (&node->locus, 0, _("unsupported type")); return -1; } grecs_txtacc_grow_char (acc, 0); url = grecs_txtacc_finish (acc, 1); val = grecs_malloc (sizeof(*val)); val->type = GRECS_TYPE_STRING; val->locus = node->locus; val->v.string = url; grecs_subtree_free (node->down); node->type = grecs_node_stmt; node->v.value = val; return 0; } static int xlat_listen_socket (struct grecs_node *node) { int rc; struct grecs_value *val; struct grecs_txtacc *acc; if (node->type != grecs_node_block) return -1; if (substatement_find (node, "type", GRECS_TYPE_STRING, 1, &val) != substatement_success) return -1; acc = grecs_txtacc_create (); rc = xlat_listen_socket_i (node, val->v.string, acc); grecs_txtacc_free (acc); return rc; } struct meta1_translator { char const *old_name; char const *new_name; int (*translate) (struct grecs_node *); }; static struct meta1_translator transtab[] = { { "start_action", "mode", xlat_start_action }, { "listen_socket", "socket", xlat_listen_socket }, { "pass_fd_socket", "pass-fd-socket" }, { "user", "user" }, { "path", "program" }, { "arguments", "command" }, { "restart_dependencies", "dependents", }, { NULL } }; static struct meta1_translator * meta1_translator_lookup (const char *name) { struct meta1_translator *p; for (p = transtab; p->old_name; p++) if (strcmp (p->old_name, name) == 0) return p; return NULL; } static int meta1_translate_stmt (struct grecs_node *stmt, struct component *comp) { struct meta1_translator *trans = meta1_translator_lookup (stmt->ident); struct grecs_keyword *kwp; if (!trans) return 0; if (trans->translate && trans->translate (stmt)) return -1; if (stmt->type != grecs_node_stmt) return -1; kwp = find_component_keyword (trans->new_name); grecs_process_ident (kwp, stmt->v.value, comp, &stmt->locus); return 0; } static int meta1_translate_node (struct grecs_node *node) { struct component *comp; struct grecs_node *stmt; size_t len; int err = 0; comp = component_create (node->ident); for (stmt = node->down; stmt; stmt = stmt->next) { if (meta1_translate_stmt (stmt, comp)) ++err; } if (err) { component_free (comp); return -1; } comp->privs.allgroups = 1; comp->dir = META1_QUEUE_DIR (); comp->redir[RETR_ERR].type = redir_file; comp->redir[RETR_ERR].v.file = NULL; len = 0; grecs_asprintf (&comp->redir[RETR_ERR].v.file, &len, "%s/%s.log", META1_QUEUE_DIR (), comp->tag); component_finish (comp, &node->locus); return 0; } static int meta1_translate_node_list (struct grecs_node *node) { int err = 0; while (node) { if (meta1_translate_node (node)) ++err; node = node->next; } return err; } int meta1_config_parse (const char *name) { struct grecs_node *subtree; int traceflags = 0; int rc; setflag (&traceflags, "META1_DEBUG_YACC", GRECS_TRACE_GRAM); setflag (&traceflags, "META1_DEBUG_LEX", GRECS_TRACE_LEX); grecs_error_count = 0; grecs_current_locus_point.file = grecs_install_text (name); grecs_current_locus_point.line = 1; grecs_current_locus_point.col = 0; subtree = grecs_meta1_parser (name, traceflags); if (!subtree) return -1; rc = meta1_translate_node_list (subtree->down); grecs_node_free (subtree); return rc; }