aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-10-12 23:41:21 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-10-12 23:42:04 +0300
commit8a4ba77068e5d7f6eab2cc1c1c10f31dcbccf7a6 (patch)
treec70f38d943c778684bb9dbfc7db018e7efb21bf1 /src
parentaf04ed630f18e9003756317cc627a11084d0d59a (diff)
downloadpies-8a4ba77068e5d7f6eab2cc1c1c10f31dcbccf7a6.tar.gz
pies-8a4ba77068e5d7f6eab2cc1c1c10f31dcbccf7a6.tar.bz2
Fix make distcheck and check-docs.
* doc/Makefile.am: Fix `check-*' goals. * doc/pies.texi: Update and rearrange material. Document new configuration. * lib/Makefile.am (libpies_a_SOURCES): Remove nls.c * src/Makefile.am (EXTRA_DIST): Remove pies.rc, add pp-setup. (INCLUDES): Add $(top_builddir)/gnu * src/pies.c: Minor changes. * src/progman.c: Minor changes. * README-hacking: New file.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/acl.c627
-rw-r--r--src/acl.h43
-rw-r--r--src/addrfmt.c133
-rw-r--r--src/diag.c131
-rw-r--r--src/meta.c127
-rw-r--r--src/pies.c68
-rw-r--r--src/pp-setup116
-rw-r--r--src/progman.c4
-rw-r--r--src/url.c212
-rw-r--r--src/userprivs.c279
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;
+ }
+ }
+}
+
diff --git a/src/pies.c b/src/pies.c
index f36c21f..87ddad3 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -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);
grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR,
DEFAULT_INCLUDE_DIR, NULL);
grecs_preprocessor = DEFAULT_PREPROCESSOR;
@@ -1178,19 +1184,18 @@ config_help ()
N_("Configuration file structure for pies.\n"
"For more information, use `info pies configuration'.");
grecs_format_docstring (stdout, docstring, 0);
- grecs_format_statement_array (stdout, pies_cfg_param, 1, 0);
+ grecs_format_statement_array (stdout, pies_keywords, 1, 0);
}
const char *program_version = "pies (" PACKAGE_STRING ")";
-const char *package_bugreport = "<" PACKAGE_BUGREPORT ">";
+const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
static char doc[] = N_("pies -- process invocation and execution supervisor");
static char args_doc[] = "";
enum
{
OPT_FOREGROUND = 256,
- OPT_LOG_TAG,
OPT_SYSLOG,
OPT_STDERR,
OPT_DUMP_PREREQ,
@@ -1210,8 +1215,6 @@ static struct argp_option options[] = {
{"foreground", OPT_FOREGROUND, 0, 0, N_("remain in foreground"), GRP + 1},
{"stderr", OPT_STDERR, NULL, 0, N_("log to stderr"),},
{"syslog", OPT_SYSLOG, NULL, 0, N_("log to syslog"),},
- {"log-tag", OPT_LOG_TAG, N_("STRING"), 0,
- N_("set the identifier used in syslog messages to STRING"), GRP + 1},
{"debug", 'x', N_("LEVEL"), 0,
N_("set debug verbosity level"), GRP + 1},
{"source-info", OPT_SOURCE_INFO, NULL, 0,
@@ -1304,10 +1307,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
force_option = 1;
break;
- case OPT_LOG_TAG:
- log_tag = arg;
- break;
-
case ARGP_KEY_INIT:
break;
@@ -1672,6 +1671,33 @@ remove_pidfile (char *name)
name, strerror (errno));
}
+const char version_etc_copyright[] =
+ /* Do *not* mark this string for translation. %s is a copyright
+ symbol suitable for this locale */
+ "Copyright %s 2009 Sergey Poznyakoff";
+
+
+static void
+version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "%s (%s) %s\n", "pies", PACKAGE, PACKAGE_VERSION);
+ /* TRANSLATORS: Translate "(C)" to the copyright symbol
+ (C-in-a-circle), if this symbol is available in the user's
+ locale. Otherwise, do not translate "(C)"; leave it as-is. */
+ fprintf (stream, version_etc_copyright, _("(C)"));
+
+ fputs (_("\
+\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+\n\
+"),
+ stream);
+
+ /* TRANSLATORS: %s denotes an author name. */
+ fprintf (stream, _("Written by %s.\n"), "Sergey Poznyakoff");
+}
int
main (int argc, char **argv)
@@ -1681,12 +1707,18 @@ main (int argc, char **argv)
extern char **environ;
set_program_name (argv[0]);
- mf_init_nls ();
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ bindtextdomain ("mailfromd", LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
mf_proctitle_init (argc, argv, environ);
/* Set default logging */
log_setup (!stderr_closed_p ());
config_init ();
+ argp_program_version_hook = version;
if (argp_parse (&argp, argc, argv, 0, &index, NULL))
exit (EX_USAGE);
diff --git a/src/pp-setup b/src/pp-setup
new file mode 100644
index 0000000..cd0264d
--- /dev/null
+++ b/src/pp-setup
@@ -0,0 +1,116 @@
+divert(-1) dnl -*- m4 -*-
+# This file is part of Pies.
+# Copyright (C) 2007, 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/>.
+
+changecom(/*,*/)
+
+/* ------------------------------
+ * Simulate --prefix-builtins.
+ * Borrowed from Autoconf
+ * ------------------------------- */
+
+define(`m4_define', defn(`define'))
+define(`m4_defn', defn(`defn'))
+define(`m4_undefine', defn(`undefine'))
+
+m4_undefine(`define')
+m4_undefine(`defn')
+m4_undefine(`undefine')
+
+/* m4_copy(SRC, DST)
+ * -----------------
+ * Define DST as the definition of SRC.
+ * What's the difference between:
+ * 1. m4_copy(`from', `to')
+ * 2. m4_define(`to', `from($@)')
+ * Well, obviously 1 is more expensive in space. Maybe 2 is more expensive
+ * in time, but because of the space cost of 1, it's not that obvious.
+ * Nevertheless, one huge difference is the handling of `$0'. If `from'
+ * uses `$0', then with 1, `to''s `$0' is `to', while it is `from' in 2.
+ * The user will certainly prefer to see `to'.
+ */
+m4_define(`m4_copy',
+`m4_define(`$2', m4_defn(`$1'))')
+
+/* m4_rename(SRC, DST)
+ * -------------------
+ * Rename the macro SRC as DST.
+ */
+m4_define(`m4_rename',
+`m4_copy(`$1', `$2')m4_undefine(`$1')')
+
+/* m4_rename_m4(MACRO-NAME)
+ * ------------------------
+ * Rename MACRO-NAME as m4_MACRO-NAME.
+ */
+m4_define(`m4_rename_m4',
+`m4_rename(`$1', `m4_$1')')
+
+/* Some m4 internals have names colliding with tokens we might use.
+ * Rename them a` la `m4 --prefix-builtins'.
+ */
+m4_rename_m4(`builtin')
+m4_rename_m4(`changecom')
+m4_rename_m4(`changequote')
+m4_rename_m4(`debugfile')
+m4_rename_m4(`debugmode')
+m4_rename_m4(`decr')
+m4_rename_m4(`divert')
+m4_rename_m4(`divnum')
+m4_rename_m4(`dumpdef')
+m4_rename_m4(`errprint')
+m4_rename_m4(`esyscmd')
+m4_rename_m4(`eval')
+m4_rename_m4(`format')
+m4_rename_m4(`ifdef')
+m4_rename_m4(`ifelse')
+m4_rename_m4(`include')
+m4_rename_m4(`incr')
+m4_rename_m4(`index')
+m4_rename_m4(`indir')
+m4_rename_m4(`len')
+m4_rename(`m4exit', `m4_exit')
+m4_rename(`m4wrap', `m4_wrap')
+m4_rename_m4(`maketemp')
+m4_rename_m4(`patsubst')
+m4_rename_m4(`popdef')
+m4_rename_m4(`pushdef')
+m4_rename_m4(`regexp')
+m4_rename_m4(`shift')
+m4_rename_m4(`sinclude')
+m4_rename_m4(`substr')
+m4_rename_m4(`symbols')
+m4_rename_m4(`syscmd')
+m4_rename_m4(`sysval')
+m4_rename_m4(`traceoff')
+m4_rename_m4(`traceon')
+m4_rename_m4(`translit')
+m4_rename_m4(`undivert')
+m4_rename_m4(`dnl')
+m4_rename_m4(`__line__')
+m4_rename_m4(`__file__')
+
+/* defined(X) -- Return true if the optional argument X is passed to
+ the function */
+m4_define(`defined',``$'#>@$1')
+
+/* printf(FMT, ...) */
+m4_define(`printf',`echo sprintf($*)')
+
+m4_define(`_', `m4_ifelse($#,1,`gettext($1)',``_'')')
+m4_define(`N_', `m4_ifelse($#,1,`$1',``N_'')')
+
+m4_divert(0)m4_dnl
diff --git a/src/progman.c b/src/progman.c
index cee6775..3211d9a 100644
--- a/src/progman.c
+++ b/src/progman.c
@@ -150,11 +150,11 @@ void
unlink_prog (struct prog *pp)
{
struct prog *x;
- if (x = pp->prev)
+ if ((x = pp->prev))
x->next = pp->next;
else
proghead = pp->next;
- if (x = pp->next)
+ if ((x = pp->next))
x->prev = pp->prev;
else
progtail = pp->prev;
diff --git a/src/url.c b/src/url.c
new file mode 100644
index 0000000..69abcfb
--- /dev/null
+++ b/src/url.c
@@ -0,0 +1,212 @@
+/* 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 <string.h>
+#include <errno.h>
+#include <limits.h>
+
+/* proto://[user[:password]@][host[:port]/]path[;arg=str[;arg=str...]
+*/
+
+static int
+alloc_string_len (char **sptr, const char *start, size_t len)
+{
+ *sptr = malloc (len + 1);
+ if (!*sptr)
+ return 1;
+ memcpy (*sptr, start, len);
+ (*sptr)[len] = 0;
+ return 0;
+}
+
+static int
+alloc_string (char **sptr, const char *start, const char *end)
+{
+ size_t len = end ? end - start : strlen (start);
+ return alloc_string_len (sptr, start, len);
+}
+
+static int
+url_parse_args (struct pies_url *url, char **str)
+{
+ struct wordsplit ws;
+
+ ws.ws_delim = ";";
+ if (wordsplit (*str, &ws, WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM))
+ return 1;
+ url->argc = ws.ws_wordc;
+ url->argv = ws.ws_wordv;
+ return 0;
+}
+
+static int
+url_parse_path (struct pies_url *url, char **str)
+{
+ char *p;
+
+ p = strchr (*str, ';');
+ if (!p)
+ p = *str + strlen (*str);
+ if (alloc_string(&url->path, *str, p))
+ return 1;
+ *str = p;
+ if (*p)
+ ++ * str;
+ return url_parse_args (url, str);
+}
+
+/* On input str points at the beginning of host part */
+static int
+url_parse_host (struct pies_url *url, char **str)
+{
+ char *s = *str;
+ size_t len = strcspn (s, "/:");
+
+ if (s[len] == ':')
+ {
+ char *q;
+ unsigned long n = strtoul (s + len + 1, &q, 10);
+ if ((*q && !strchr("/;:", *q)) || n > USHRT_MAX)
+ return 1;
+ url->port = n;
+ *str = q + strcspn(q, "/");
+ }
+ else
+ *str = s + len;
+ if (alloc_string_len (&url->host, s, len))
+ return 1;
+ if (**str)
+ {
+ ++*str;
+ return url_parse_path (url, str);
+ }
+ return 0;
+}
+
+/* On input str points past the mech:// part */
+static int
+url_parse_user (struct pies_url *url, char **str)
+{
+ size_t len = strcspn (*str, ":;@/");
+ char *p = *str + len;
+
+ switch (*p)
+ {
+ case ';':
+ case ':':
+ len = strcspn (p + 1, "@/:");
+ if (p[len+1] == '@')
+ {
+ if (alloc_string_len(&url->passwd, p + 1, len))
+ return 1;
+ if (alloc_string (&url->user, *str, p))
+ return 1;
+ *str = p + len + 2;
+ }
+ break;
+
+ case '@':
+ if (alloc_string (&url->user, *str, p))
+ return 1;
+ url->passwd = NULL;
+ *str = p + 1;
+ }
+ return url_parse_host (url, str);
+}
+
+static int
+url_parse_proto (struct pies_url *url, const char *str)
+{
+ char *p;
+
+ if (!str)
+ {
+ errno = EINVAL;
+ return 1;
+ }
+
+ p = strchr (str, ':');
+ if (!p)
+ {
+ errno = EINVAL;
+ return 1;
+ }
+
+ alloc_string (&url->proto, str, p);
+
+ /* Skip slashes */
+ for (p++; *p == '/'; p++)
+ ;
+ return url_parse_user (url, &p);
+}
+
+void
+pies_url_destroy (struct pies_url **purl)
+{
+ int i;
+ struct pies_url *url = *purl;
+
+ free (url->string);
+ free (url->proto);
+ free (url->host);
+ free (url->path);
+ free (url->user);
+ free (url->passwd);
+ for (i = 0; i < url->argc; i++)
+ free (url->argv[i]);
+ free (url->argv);
+ free(url);
+ *purl = NULL;
+}
+
+int
+pies_url_create (struct pies_url **purl, const char *str)
+{
+ int rc;
+ struct pies_url *url;
+
+ url = malloc (sizeof (*url));
+ if (!url)
+ return 1;
+ memset (url, 0, sizeof(*url));
+ rc = url_parse_proto (url, str);
+ if (rc)
+ pies_url_destroy (&url);
+ else
+ {
+ url->string = strdup (str);
+ *purl = url;
+ }
+ return rc;
+}
+
+const char *
+pies_url_get_arg (struct pies_url *url, const char *argname)
+{
+ int i;
+ size_t arglen = strlen (argname);
+ for (i = 0; i < url->argc; i++)
+ {
+ size_t len = strcspn (url->argv[i], "=");
+ if (len == arglen && memcmp (url->argv[i], argname, arglen) == 0)
+ return url->argv[i] + len + 1;
+ }
+ return NULL;
+}
diff --git a/src/userprivs.c b/src/userprivs.c
new file mode 100644
index 0000000..3abb001
--- /dev/null
+++ b/src/userprivs.c
@@ -0,0 +1,279 @@
+/* This file is part of Pies.
+ Copyright (C) 2007, 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/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <stdbool.h>
+#include "pies.h"
+
+static bool
+str_eq (const void *elt1, const void *elt2)
+{
+ return strcmp (elt1, elt2) == 0;
+}
+
+void
+str_dispose (const void *elt)
+{
+ free ((void*)elt);
+}
+
+gl_list_t
+get_user_groups (gl_list_t init_list, const char *user)
+{
+ int rc;
+ struct group *gr;
+ gl_list_t list;
+
+ list = gl_list_create_empty(&gl_linked_list_implementation,
+ str_eq,
+ NULL,
+ str_dispose,
+ false);
+
+ if (init_list)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (init_list);
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ char *s = xstrdup (p);
+ if (!gl_list_add_last (list, s))
+ free (s);
+ }
+ gl_list_iterator_free (&itr);
+ }
+
+ setgrent ();
+ for (rc = 0; rc == 0 && (gr = getgrent ());)
+ {
+ char **p;
+ for (p = gr->gr_mem; *p; p++)
+ if (strcmp (*p, user) == 0)
+ {
+ char *s = xstrdup (gr->gr_name);
+ if (!gl_list_add_last (list, s))
+ free (s);
+ break;
+ }
+ }
+ endgrent ();
+ return list;
+}
+
+/* Switch to the given UID/GID */
+int
+switch_to_privs (uid_t uid, gid_t gid, gl_list_t retain_groups)
+{
+ int rc = 0;
+ gid_t *emptygidset;
+ size_t size = 1, j = 1;
+
+ if (uid == 0)
+ {
+ logmsg (LOG_ERR, _("Refusing to run as root"));
+ return 1;
+ }
+
+ /* Create a list of supplementary groups */
+ size = 1 + (retain_groups ? gl_list_size (retain_groups) : 0);
+ emptygidset = xcalloc (size, sizeof emptygidset[0]);
+ emptygidset[0] = gid ? gid : getegid ();
+
+ if (retain_groups)
+ {
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (retain_groups);
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ struct group *group = getgrnam ((const char*)p);
+ if (!group)
+ {
+ logmsg (LOG_ERR, _("unknown group: %s"), (const char*)p);
+ free (emptygidset);
+ return 1;
+ }
+ emptygidset[j++] = group->gr_gid;
+ }
+ gl_list_iterator_free (&itr);
+ }
+
+ /* Reset group permissions */
+ if (geteuid () == 0 && setgroups (j, emptygidset))
+ {
+ logmsg (LOG_ERR, _("setgroups(1, %lu) failed: %s"),
+ (unsigned long) emptygidset[0], strerror (errno));
+ rc = 1;
+ }
+ free (emptygidset);
+
+ /* Switch to the user's gid. On some OSes the effective gid must
+ be reset first */
+
+#if defined(HAVE_SETEGID)
+ if ((rc = setegid (gid)) < 0)
+ logmsg (LOG_ERR, _("setegid(%lu) failed: %s"),
+ (unsigned long) gid, strerror (errno));
+#elif defined(HAVE_SETREGID)
+ if ((rc = setregid (gid, gid)) < 0)
+ logmsg (LOG_ERR, _("setregid(%lu,%lu) failed: %s"),
+ (unsigned long) gid, (unsigned long) gid, strerror (errno));
+#elif defined(HAVE_SETRESGID)
+ if ((rc = setresgid (gid, gid, gid)) < 0)
+ logmsg (LOG_ERR, _("setresgid(%lu,%lu,%lu) failed: %s"),
+ (unsigned long) gid,
+ (unsigned long) gid, (unsigned long) gid, strerror (errno));
+#endif
+
+ if (rc == 0 && gid != 0)
+ {
+ if ((rc = setgid (gid)) < 0 && getegid () != gid)
+ logmsg (LOG_ERR, _("setgid(%lu) failed: %s"),
+ (unsigned long) gid, strerror (errno));
+ if (rc == 0 && getegid () != gid)
+ {
+ logmsg (LOG_ERR, _("Cannot set effective gid to %lu"),
+ (unsigned long) gid);
+ rc = 1;
+ }
+ }
+
+ /* Now reset uid */
+ if (rc == 0 && uid != 0)
+ {
+ uid_t euid;
+
+ if (setuid (uid)
+ || geteuid () != uid
+ || (getuid () != uid && (geteuid () == 0 || getuid () == 0)))
+ {
+
+#if defined(HAVE_SETREUID)
+ if (geteuid () != uid)
+ {
+ if (setreuid (uid, -1) < 0)
+ {
+ logmsg (LOG_ERR, _("setreuid(%lu,-1) failed: %s"),
+ (unsigned long) uid, strerror (errno));
+ rc = 1;
+ }
+ if (setuid (uid) < 0)
+ {
+ logmsg (LOG_ERR, _("second setuid(%lu) failed: %s"),
+ (unsigned long) uid, strerror (errno));
+ rc = 1;
+ }
+ }
+ else
+#endif
+ {
+ logmsg (LOG_ERR, _("setuid(%lu) failed: %s"),
+ (unsigned long) uid, strerror (errno));
+ rc = 1;
+ }
+ }
+
+ euid = geteuid ();
+ if (uid != 0 && setuid (0) == 0)
+ {
+ logmsg (LOG_ERR, _("seteuid(0) succeeded when it should not"));
+ rc = 1;
+ }
+ else if (uid != euid && setuid (euid) == 0)
+ {
+ logmsg (LOG_ERR, _("Cannot drop non-root setuid privileges"));
+ rc = 1;
+ }
+
+ }
+
+ return rc;
+}
+
+
+void
+pies_priv_setup (struct pies_privs *privs)
+{
+ struct passwd *pw;
+ gl_list_t grplist = 0;
+
+ if (!privs || !privs->user)
+ return;
+
+ pw = getpwnam (privs->user);
+ if (!pw)
+ {
+ logmsg (LOG_ERR, _("no such user: %s"), privs->user);
+ exit (EX_CONFIG);
+ }
+
+ if (privs->allgroups)
+ grplist = get_user_groups (privs->groups, privs->user);
+ if (switch_to_privs (pw->pw_uid, pw->pw_gid,
+ grplist ? grplist : privs->groups))
+ exit (EX_SOFTWARE);
+ if (grplist)
+ gl_list_free (grplist);
+}
+
+
+void
+pies_epriv_setup (struct pies_privs *privs)
+{
+ uid_t uid;
+ gid_t gid;
+
+ if (privs)
+ {
+ struct passwd *pw;
+ if (!privs->user)
+ return;
+
+ pw = getpwnam (privs->user);
+ if (!pw)
+ {
+ logmsg (LOG_ERR, _("No such user: %s"), privs->user);
+ exit (EX_CONFIG);
+ }
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+ }
+ else
+ {
+ uid = 0;
+ gid = 0;
+ }
+
+ if (setegid (gid))
+ {
+ logmsg (LOG_ERR, _("Cannot switch to EGID %lu: %s"),
+ (unsigned long) gid, strerror (errno));
+ exit (EX_USAGE);
+ }
+ if (seteuid (uid))
+ {
+ logmsg (LOG_ERR, _("Cannot switch to EUID %lu: %s"),
+ (unsigned long) uid, strerror (errno));
+ exit (EX_USAGE);
+ }
+}

Return to:

Send suggestions and report system problems to the System administrator.