diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/acl.c | 627 | ||||
-rw-r--r-- | src/acl.h | 43 | ||||
-rw-r--r-- | src/addrfmt.c | 133 | ||||
-rw-r--r-- | src/diag.c | 131 | ||||
-rw-r--r-- | src/meta.c | 127 | ||||
-rw-r--r-- | src/pies.c | 68 | ||||
-rw-r--r-- | src/pp-setup | 116 | ||||
-rw-r--r-- | src/progman.c | 4 | ||||
-rw-r--r-- | src/url.c | 212 | ||||
-rw-r--r-- | src/userprivs.c | 279 |
11 files changed, 1722 insertions, 26 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c0c3b8a..970a8b3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,20 +37,16 @@ noinst_HEADERS = \ 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 incdir=$(pkgdatadir)/$(VERSION)/include inc_DATA = pp-setup +EXTRA_DIST = pp-setup INCLUDES = \ -I$(top_srcdir)/lib\ -I$(top_srcdir)/gnu\ + -I$(top_builddir)/gnu\ -I$(top_srcdir)/grecs/src LDADD = \ diff --git a/src/acl.c b/src/acl.c new file mode 100644 index 0000000..8561967 --- /dev/null +++ b/src/acl.c @@ -0,0 +1,627 @@ +/* This file is part of Pies + Copyright (C) 2009 Sergey Poznyakoff + + 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. + + 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 Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "pies.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <hash.h> + +struct pies_sockaddr +{ + unsigned netmask; + int salen; + struct sockaddr sa; +}; + +struct acl_entry +{ + grecs_locus_t locus; + int allow; + int authenticated; + pies_acl_t acl; + gl_list_t groups; + gl_list_t sockaddrs; +}; + +struct pies_acl +{ + char *name; + grecs_locus_t locus; + gl_list_t list; +}; + + + +/* ACL creation */ + +pies_acl_t +pies_acl_create (const char *name, grecs_locus_t *locus) +{ + pies_acl_t acl = xmalloc (sizeof (acl[0])); + acl->name = xstrdup (name); + acl->locus = *locus; + acl->list = gl_list_create_empty(&gl_linked_list_implementation, + NULL, + NULL, + NULL, + false); + return acl; +} + +static struct pies_sockaddr * +create_acl_sockaddr (int family, int len) +{ + struct pies_sockaddr *p = xzalloc (sizeof (*p)); + p->salen = len; + p->sa.sa_family = family; + return p; +} + +/* allow|deny [all|authenticated|group <grp: list>] + [acl <name: string>] [from <addr: list>] */ + +static int +_parse_token (struct acl_entry *entry, const grecs_value_t *value) +{ + if (strcmp (value->v.string, "all") == 0 + || strcmp (value->v.string, "any") == 0) + /* FIXME: Nothing? */ ; + else if (strcmp (value->v.string, "auth") == 0 + || strcmp (value->v.string, "authenticated") == 0) + entry->authenticated = 1; + else + return 1; + return 0; +} + +static int +_parse_sockaddr (struct acl_entry *entry, const grecs_value_t *value) +{ + struct pies_sockaddr *sptr; + const char *string; + + if (assert_grecs_value_type (&entry->locus, value, GRECS_TYPE_STRING)) + return 1; + + string = value->v.string; + + if (string[0] == '/') + { + size_t len; + struct sockaddr_un *s_un; + + len = strlen (string); + if (len >= sizeof (s_un->sun_path)) + { + grecs_error (&entry->locus, 0, + _("socket name too long: `%s'"), string); + return 1; + } + sptr = create_acl_sockaddr (AF_UNIX, sizeof (s_un)); + s_un = (struct sockaddr_un *) &sptr->sa; + memcpy (s_un->sun_path, string, len); + s_un->sun_path[len] = 0; + } + else + { + struct in_addr addr; + struct sockaddr_in *s_in; + char *p = strchr (string, '/'); + + if (p) + *p = 0; + + if (inet_aton (string, &addr) == 0) + { + struct hostent *hp = gethostbyname (string); + if (!hp) + { + grecs_error (&entry->locus, 0, + _("cannot resolve host name: `%s'"), string); + if (p) + *p = '/'; + return 1; + } + memcpy (&addr.s_addr, hp->h_addr, sizeof (addr.s_addr)); + } + addr.s_addr = ntohl (addr.s_addr); + + sptr = create_acl_sockaddr (AF_INET, sizeof (s_in)); + s_in = (struct sockaddr_in *) &sptr->sa; + s_in->sin_addr = addr; + + if (p) + { + *p++ = '/'; + char *q; + unsigned netlen; + + netlen = strtoul (p, &q, 10); + if (*q == 0) + { + if (netlen == 0) + sptr->netmask = 0; + else + { + sptr->netmask = 0xfffffffful >> (32 - netlen); + sptr->netmask <<= (32 - netlen); + sptr->netmask = htonl (sptr->netmask); + } + } + else if (*q == '.') + { + struct in_addr addr; + + if (inet_aton (p, &addr) == 0) + { + grecs_error (&entry->locus, 0, + _("invalid netmask: `%s'"), p); + return 1; + } + sptr->netmask = addr.s_addr; + } + else + { + grecs_error (&entry->locus, 0, _("invalid netmask: `%s'"), p); + return 1; + } + } + else + sptr->netmask = 0xfffffffful; + } + gl_list_add_last (entry->sockaddrs, sptr); + return 0; +} + +static int +_parse_from (struct acl_entry *entry, size_t argc, const grecs_value_t *argv) +{ + if (argc == 0) + return 0; + else if (argv->type == GRECS_TYPE_LIST) + { + grecs_error (&entry->locus, 0, _("expected `from', but found list")); + return 1; + } + else if (strcmp (argv->v.string, "from")) + { + grecs_error (&entry->locus, 0, _("expected `from', but found `%s'"), + argv->v.string); + return 1; + } + argc--; + argv++; + + if (argc == 0) + { + grecs_error (&entry->locus, 0, + _("unexpected end of statement after `from'")); + return 1; + } + + entry->sockaddrs = gl_list_create_empty(&gl_linked_list_implementation, + NULL, + NULL, + NULL, + false); + if (argv->type == GRECS_TYPE_STRING) + { + if (_parse_sockaddr (entry, argv)) + return 1; + } + else + { + gl_list_iterator_t itr = gl_list_iterator (argv->v.list); + const void *p; + int rc = 0; + while (gl_list_iterator_next (&itr, &p, NULL)) + rc += _parse_sockaddr (entry, (const grecs_value_t*) p); + gl_list_iterator_free (&itr); + if (rc) + return rc; + } + + if (argc - 1) + { + grecs_warning (&entry->locus, 0, _("junk after `from' list")); + return 1; + } + return 0; +} + +static int +_parse_sub_acl (struct acl_entry *entry, size_t argc, grecs_value_t *argv) +{ + if (argc == 0) + return 0; + if (strcmp (argv->v.string, "acl") == 0) + { + argc--; + argv++; + if (argc == 0) + { + grecs_error (&entry->locus, 0, + _("expected ACL name, but found end of statement")); + return 1; + } + + if (argv->type != GRECS_TYPE_STRING) + { + grecs_error (&entry->locus, 0, + _("expected string, but found list")); + return 1; + } + + entry->acl = pies_acl_lookup (argv->v.string); + + if (!entry->acl) + { + grecs_error (&entry->locus, 0, _("ACL not defined: `%s'"), + argv->v.string); + return 1; + } + argc--; + argv++; + } + return _parse_from (entry, argc, argv); +} + +static int +_parse_group (struct acl_entry *entry, size_t argc, grecs_value_t * argv) +{ + if (strcmp (argv->v.string, "group") == 0) + { + argc--; + argv++; + if (argc == 0) + { + grecs_error (&entry->locus, 0, + _("expected group list, but found end of statement")); + return 1; + } + if (argv->type == GRECS_TYPE_STRING) + { + entry->groups = gl_list_create_empty(&gl_linked_list_implementation, + NULL, + NULL, + NULL, + false); + gl_list_add_last (entry->groups, (void *) argv->v.string); + } + else + entry->groups = argv->v.list; + argc--; + argv++; + } + return _parse_sub_acl (entry, argc, argv); +} + +static int +_parse_acl (struct acl_entry *entry, size_t argc, grecs_value_t *argv) +{ + if (assert_grecs_value_type (&entry->locus, argv, GRECS_TYPE_STRING)) + return 1; + else if (_parse_token (entry, argv) == 0) + return _parse_sub_acl (entry, argc - 1, argv + 1); + else + return _parse_group (entry, argc, argv); +} + +int +parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl, + grecs_value_t *value) +{ + struct acl_entry *entry = xzalloc (sizeof (*entry)); + + entry->locus = *locus; + entry->allow = allow; + + switch (value->type) + { + case GRECS_TYPE_STRING: + if (_parse_token (entry, value)) + { + grecs_error (&entry->locus, 0, _("unknown word `%s'"), + value->v.string); + return 1; + } + break; + + case GRECS_TYPE_ARRAY: + if (_parse_acl (entry, value->v.arg.c, value->v.arg.v)) + return 1; + break; + + case GRECS_TYPE_LIST: + grecs_error (locus, 0, _("unexpected list")); + return 1; + } + gl_list_add_last (acl->list, entry); + return 0; +} + +int +acl_section_parser (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, + void *cb_data) +{ + void **pdata = cb_data; + pies_acl_t acl; + + switch (cmd) + { + case grecs_callback_section_begin: + if (value->type != GRECS_TYPE_STRING) + grecs_error (locus, 0, _("ACL name must be a string")); + else if (!value->v.string) + grecs_error (locus, 0, _("missing ACL name")); + else + { + grecs_locus_t defn_loc; + acl = pies_acl_create (value->v.string, locus); + if (pies_acl_install (acl, &defn_loc)) + { + grecs_error (locus, 0, + _("redefinition of ACL %s"), + value->v.string); + grecs_error (&defn_loc, 0, + _("location of the previous definition")); + return 1; + } + *pdata = acl; + } + break; + + case grecs_callback_section_end: + case grecs_callback_set_value: + break; + } + return 0; +} + +static int +allow_cb (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, + void *cb_data) +{ + pies_acl_t acl = varptr; + + if (cmd != grecs_callback_set_value) + { + grecs_error (locus, 0, _("unexpected block statement")); + return 1; + } + parse_acl_line (locus, 1, acl, value); + return 0; +} + +static int +deny_cb (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, + void *cb_data) +{ + pies_acl_t acl = varptr; + if (cmd != grecs_callback_set_value) + { + grecs_error (locus, 0, _("unexpected block statement")); + return 1; + } + parse_acl_line (locus, 0, acl, value); + return 0; +} + +struct grecs_keyword acl_keywords[] = { + { "allow", N_("[all|authenticated|group <grp: list>] [from <addr: list>]"), + N_("Allow access"), + grecs_type_string, NULL, 0, + allow_cb }, + { "deny", N_("[all|authenticated|group <grp: list>] [from <addr: list>]"), + N_("Deny access"), + grecs_type_string, NULL, 0, + deny_cb }, + { NULL } +}; + + + + +/* ACL verification */ + +#define S_UN_NAME(sa, salen) \ + ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path) + +static int +_check_sockaddr (struct pies_sockaddr *sptr, struct acl_input *input) +{ + if (sptr->sa.sa_family != input->addr->sa_family) + return 0; + + switch (sptr->sa.sa_family) + { + case AF_INET: + { + struct sockaddr_in *sin_clt = (struct sockaddr_in *) input->addr; + struct sockaddr_in *sin_item = (struct sockaddr_in *) &sptr->sa; + + if (ntohl (sin_clt->sin_addr.s_addr) == + (sin_item->sin_addr.s_addr & sptr->netmask)) + return 1; + break; + } + + case AF_UNIX: + { + struct sockaddr_un *sun_clt = (struct sockaddr_un *) input->addr; + struct sockaddr_un *sun_item = (struct sockaddr_un *) &sptr->sa; + + if (S_UN_NAME (sun_clt, input->addrlen)[0] + && S_UN_NAME (sun_item, sptr->salen)[0] + && strcmp (sun_clt->sun_path, sun_item->sun_path) == 0) + return 1; + } + } + return 0; +} + +static int +match_group (const char **groups, const char *arg) +{ + for (; *groups; groups++) + if (strcmp (*groups, arg) == 0) + return 1; + return 0; +} + +static int +_acl_check (struct acl_entry *ent, struct acl_input *input) +{ + int result = 1; + + if (ent->authenticated) + { + result = input->user != NULL; + if (!result) + return result; + } + + if (ent->groups) + { + const void *p; + gl_list_iterator_t itr = gl_list_iterator (ent->groups); + while (result && gl_list_iterator_next (&itr, &p, NULL)) + result = match_group (input->groups, p); + gl_list_iterator_free (&itr); + if (!result) + return result; + } + + result = pies_acl_check (ent->acl, input, 1); + if (!result) + return result; + + if (ent->sockaddrs) + { + const void *p; + gl_list_iterator_t itr = gl_list_iterator (ent->sockaddrs); + result = 0; + while (gl_list_iterator_next (&itr, &p, NULL)) + { + if (_check_sockaddr ((struct pies_sockaddr *)p, input)) + break; + } + gl_list_iterator_free (&itr); + } + + return result; +} + +static int +_acl_check_cb (struct acl_entry *ent, struct acl_input *input, int *pres) +{ + int result = _acl_check (ent, input); + debug (10, ("%s:%d: %s", ent->locus.file, ent->locus.line, + /* TRANSLATIONS: `MATCHES' is the verb `match' in 2nd person. + E.g., in French: CONCORD AVEC */ + result ? _("MATCHES") : _("does not match"))); + + if (result) + { + *pres = ent->allow; + return 1; + } + return 0; +} + +int +pies_acl_check (pies_acl_t acl, struct acl_input *input, int result) +{ + if (acl) + { + const void *p; + gl_list_iterator_t itr = gl_list_iterator (acl->list); + while (gl_list_iterator_next (&itr, &p, NULL) + && _acl_check_cb ((struct acl_entry *)p, input, &result)) + ; + gl_list_iterator_free (&itr); + } + return result; +} + + +/* Hash table */ + +static Hash_table *acl_table; + +/* Calculate the hash of a string. */ +static size_t +acl_hasher (void const *data, unsigned n_buckets) +{ + const struct pies_acl *p = data; + return hash_string (p->name, n_buckets); +} + +/* Compare two strings for equality. */ +static bool +acl_compare (void const *data1, void const *data2) +{ + const struct pies_acl *p1 = data1; + const struct pies_acl *p2 = data2; + return strcasecmp (p1->name, p2->name) == 0; +} + +int +pies_acl_install (pies_acl_t acl, grecs_locus_t * locus) +{ + pies_acl_t ret; + if (!((acl_table + || (acl_table = hash_initialize (0, 0, + acl_hasher, + acl_compare, 0))) + && (ret = hash_insert (acl_table, acl)))) + xalloc_die (); + + if (ret != acl) + { + if (locus) + *locus = ret->locus; + return 1; + } + return 0; +} + +pies_acl_t +pies_acl_lookup (const char *name) +{ + struct pies_acl samp; + if (!acl_table) + return NULL; + samp.name = (char *) name; + return hash_lookup (acl_table, &samp); +} diff --git a/src/acl.h b/src/acl.h new file mode 100644 index 0000000..8d7725e --- /dev/null +++ b/src/acl.h @@ -0,0 +1,43 @@ +/* This file is part of Pies + Copyright (C) 2009 Sergey Poznyakoff + + 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. + + 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 Pies. If not, see <http://www.gnu.org/licenses/>. */ + +/* acl.c */ +typedef struct pies_acl *pies_acl_t; + +struct acl_input +{ + struct sockaddr *addr; + socklen_t addrlen; + const char *user; + const char **groups; +}; + +pies_acl_t pies_acl_create (const char *name, grecs_locus_t *locus); +int pies_acl_check (pies_acl_t acl, struct acl_input *input, int result); +int parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl, + grecs_value_t *value); +pies_acl_t pies_acl_lookup (const char *name); +int pies_acl_install (pies_acl_t acl, grecs_locus_t * locus); + +int assert_grecs_value_type (grecs_locus_t *locus, + const grecs_value_t *value, int type); + +extern struct grecs_keyword acl_keywords[]; +extern int acl_section_parser (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, + void *cb_data); diff --git a/src/addrfmt.c b/src/addrfmt.c new file mode 100644 index 0000000..0687445 --- /dev/null +++ b/src/addrfmt.c @@ -0,0 +1,133 @@ +/* This file is part of Pies + Copyright (C) 2009 Sergey Poznyakoff + + 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. + + 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 Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "pies.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +int +str2port (char *str) +{ + struct servent *serv; + char *p; + int port; + + /* First try to read it from /etc/services */ + serv = getservbyname (str, "tcp"); + + if (serv != NULL) + port = ntohs(serv->s_port); + else + { + unsigned long l; + /* Not in services, maybe a number? */ + l = strtoul (str, &p, 0); + + if (*p || l < 0 || l > USHRT_MAX) + return -1; + + port = l; + } + + return port; +} + +static size_t +_my_stpcpy (char **pbuf, size_t *psize, const char *src) +{ + size_t slen = strlen (src); + if (pbuf == NULL || *pbuf == NULL) + return slen; + else + { + char *buf = *pbuf; + size_t size = *psize; + if (size > slen) + size = slen; + memcpy (buf, src, size); + *psize -= size; + *pbuf += size; + if (*psize) + **pbuf = 0; + else + (*pbuf)[-1] = 0; + return size; + } +} + +#define S_UN_NAME(sa, salen) \ + ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path) + +void +sockaddr_to_str (const struct sockaddr *sa, int salen, + char *bufptr, size_t buflen, + size_t *plen) +{ + char buf[INT_BUFSIZE_BOUND (uintmax_t)]; /* FIXME: too much */ + size_t len = 0; + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in s_in = *(struct sockaddr_in *)sa; + len += _my_stpcpy (&bufptr, &buflen, inet_ntoa(s_in.sin_addr)); + len += _my_stpcpy (&bufptr, &buflen, ":"); + len += _my_stpcpy (&bufptr, &buflen, + umaxtostr(ntohs (s_in.sin_port), buf)); + break; + } + + case AF_UNIX: + { + struct sockaddr_un *s_un = (struct sockaddr_un *)sa; + if (S_UN_NAME(s_un, salen)[0] == 0) + len += _my_stpcpy (&bufptr, &buflen, "anonymous socket"); + else + { + len += _my_stpcpy (&bufptr, &buflen, "socket "); + len += _my_stpcpy (&bufptr, &buflen, s_un->sun_path); + } + break; + } + + default: + len += _my_stpcpy (&bufptr, &buflen, "{Unsupported family: "); + len += _my_stpcpy (&bufptr, &buflen, umaxtostr (sa->sa_family, buf)); + len += _my_stpcpy (&bufptr, &buflen, "}"); + } + if (plen) + *plen = len + 1; +} + +char * +sockaddr_to_astr (const struct sockaddr *sa, int salen) +{ + size_t size; + char *p; + + sockaddr_to_str(sa, salen, NULL, 0, &size); + p = xmalloc (size); + sockaddr_to_str(sa, salen, p, size, NULL); + return p; +} diff --git a/src/diag.c b/src/diag.c new file mode 100644 index 0000000..8ae3911 --- /dev/null +++ b/src/diag.c @@ -0,0 +1,131 @@ +/* This file is part of Pies. + Copyright (C) 2009 Sergey Poznyakoff + + 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. + + 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 Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#include "pies.h" +#include "xvasprintf.h" + +unsigned debug_level; +int source_info_option; + +void +syslog_printer (int prio, const char *fmt, va_list ap) +{ +#if HAVE_VSYSLOG + vsyslog (prio, fmt, ap); +#else + char buf[128]; + vsnprintf (buf, sizeof buf, fmt, ap); +#endif +} + +void +vlogmsg (int prio, const char *fmt, va_list ap) +{ + if (log_to_stderr) + { + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + } + else + syslog_printer (prio, fmt, ap); +} + +void +logmsg (int prio, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vlogmsg (prio, fmt, ap); + va_end (ap); +} + +void +debug_msg (const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vlogmsg (LOG_DEBUG, fmt, ap); + va_end (ap); +} + + +static struct obstack log_stk; +static int log_stk_init; + +void +logmsg_vprintf (int prio, const char *fmt, va_list ap) +{ + char *str, *p; + + str = xvasprintf (fmt, ap); + + if (!log_stk_init) + { + obstack_init (&log_stk); + log_stk_init = 1; + } + for (p = str; *p; ) + { + size_t len = strcspn (p, "\n"); + if (len) + obstack_grow (&log_stk, p, len); + p += len; + if (*p) + { + char *msg; + + obstack_1grow (&log_stk, 0); + msg = obstack_finish (&log_stk); + logmsg (prio, "%s", msg); + obstack_free (&log_stk, msg); + p++; + } + } + free (str); +} + +void +logmsg_printf (int prio, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + logmsg_vprintf (prio, fmt, ap); + va_end (ap); +} + +void +grecs_print_diag (grecs_locus_t *locus, int err, int errcode, const char *msg) +{ + if (locus) + { + if (errcode) + logmsg (err ? LOG_ERR : LOG_WARNING, "%s:%lu: %s: %s", + locus->file, (unsigned long)locus->line, msg, + strerror (errcode)); + else + logmsg (err ? LOG_ERR : LOG_WARNING, "%s:%lu: %s", + locus->file, (unsigned long)locus->line, msg); + } + else + { + if (errcode) + logmsg (err ? LOG_ERR : LOG_WARNING, "%s: %s", msg, + strerror (errcode)); + else + logmsg (err ? LOG_ERR : LOG_WARNING, "%s", msg); + } +} + diff --git a/src/meta.c b/src/meta.c new file mode 100644 index 0000000..9acc3bb --- /dev/null +++ b/src/meta.c @@ -0,0 +1,127 @@ +/* This file is part of Pies. + Copyright (C) 2008, 2009 Sergey Poznyakoff + + 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. + + 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 Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#include "pies.h" +#include <c-ctype.h> + +static const char * +meta_expand (struct metadef *def, void *data) +{ + if (!def->value) + { + if (def->expand) + return def->expand (def, data); + def->value = "INTERNAL ERROR: NONEXPANDABLE DATA"; + } + return def->value; +} + +static const char * +find_expansion_char (int c, struct metadef *def, void *data) +{ + for (; def->kw; def++) + if (def->kw[1] == 0 && def->kw[0] == c) + return meta_expand (def, data); + return NULL; +} + +static const char * +find_expansion_word (const char *kw, size_t len, + struct metadef *def, void *data) +{ + for (; def->kw; def++) + if (strlen (def->kw) == len && memcmp (def->kw, kw, len) == 0) + return meta_expand (def, data); + return NULL; +} + +char * +meta_expand_string (const char *string, struct metadef *def, void *data) +{ + const char *p, *s; + char *res; + struct obstack stk; + + if (!string) + return NULL; + + obstack_init (&stk); + + for (p = string; *p;) + { + char *e; + size_t len = strcspn (p, "$"); + + obstack_grow (&stk, p, len); + p += len; + if (*p == '$') + { + switch (*++p) + { + case '$': + obstack_grow (&stk, p, 1); + p++; + break; + + case '{': + e = strchr (p + 1, '}'); + if (e && (s = find_expansion_word (p + 1, e - p - 1, def, data))) + { + obstack_grow (&stk, s, strlen (s)); + p = e + 1; + } + else + { + obstack_grow (&stk, p - 1, 2); + p++; + } + break; + + default: + if ((s = find_expansion_char (*p, def, data)) != NULL) + len = strlen (s); + else + { + s = p - 1; + len = 1; + } + + obstack_grow (&stk, s, len); + p++; + } + } + else + obstack_grow (&stk, p, 1); + } + obstack_1grow (&stk, 0); + res = xstrdup (obstack_finish (&stk)); + obstack_free (&stk, NULL); + return res; +} + +void +meta_free (struct metadef *def) +{ + for (; def->kw; def++) + { + if (def->storage) + { + free (def->storage); + def->value = def->storage = NULL; + } + } +} + @@ -15,6 +15,8 @@ along with Pies. If not, see <http://www.gnu.org/licenses/>. */ #include "pies.h" +#include <locale.h> +#include <configmake.h> #include "meta1lex.h" char *conffile = SYSCONFDIR "/pies.conf"; @@ -106,7 +108,7 @@ _cb_action (enum grecs_callback_command cmd, return 0; } -struct grecs_keyword return_code_cfg_param[] = { +struct grecs_keyword return_code_keywords[] = { {"action", /* TRANSLATORS: disable and restart are keywords, do not translate them. */ N_("arg: {disable | restart}"), @@ -718,7 +720,7 @@ _cb_limits (enum grecs_callback_command cmd, return 0; } -struct grecs_keyword component_cfg_param[] = { +struct grecs_keyword component_keywords[] = { {"mode", /* TRANSLATORS: The words between '{' and '}' are keywords, do not translate them. */ @@ -868,7 +870,7 @@ struct grecs_keyword component_cfg_param[] = { N_("<tag: exit-code-list>"), NULL, /* FIXME: Docstring? */ grecs_type_section, NULL, 0, - return_code_section_parser, NULL, return_code_cfg_param}, + return_code_section_parser, NULL, return_code_keywords}, {NULL} }; @@ -877,7 +879,7 @@ find_component_keyword (const char *ident) { struct grecs_keyword *kwp; - for (kwp = component_cfg_param; kwp->ident; kwp++) + for (kwp = component_keywords; kwp->ident; kwp++) if (strcmp (kwp->ident, ident) == 0) return kwp; return NULL; @@ -1028,6 +1030,10 @@ component_section_parser (enum grecs_callback_command cmd, case grecs_callback_section_end: comp = *(struct component **) section_data; component_finish (comp, locus); + break; + + case grecs_callback_set_value: + grecs_error (locus, 0, _("expected block statement")); } return 0; } @@ -1064,13 +1070,13 @@ _cm_include_meta1 (enum grecs_callback_command cmd, return 0; } -struct grecs_keyword pies_cfg_param[] = { +struct grecs_keyword pies_keywords[] = { /* FIXME */ {"component", N_("<tag: string>"), NULL, /* FIXME: Docstring */ grecs_type_section, NULL, 0, - component_section_parser, NULL, component_cfg_param}, + component_section_parser, NULL, component_keywords}, {"syslog", NULL, N_("Configure syslog logging"), @@ -1140,7 +1146,7 @@ struct grecs_keyword pies_cfg_param[] = { N_("<tag: exit-code-list>"), NULL, /* FIXME: Docstring? */ grecs_type_section, &default_component, 0, - return_code_section_parser, NULL, return_code_cfg_param}, + return_code_section_parser, NULL, return_code_keywords}, {"acl", N_("name: string"), N_("Define ACL."), @@ -1164,7 +1170,7 @@ struct grecs_keyword pies_cfg_param[] = { void config_init () { - grecs_set_keywords (pies_cfg_param); + grecs_set_keywords (pies_keywords); |