From 00e6c3c3ed06a258a02943fc49fa7c528025d747 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 31 Dec 2015 13:59:18 +0200 Subject: Command-line control interface. * configure.ac (DEFAULT_CONTROL_URL): New subst variable. * grecs: Upgrade. * ident/pam.c (overwrite_and_free): Free ptr. * lib/Makefile.am: Add new sources. * src/addrfmt.c: Move to lib/addrfmt.c * lib/grecsasrt.c: New file. * lib/grecsasrt.h: New file. * lib/mkfilename.c: New file. * lib/netrc.c: New file. * lib/pp.c: New file. * lib/split3.c: New file. * src/url.c: Move from lib/url.c (pies_url_free_user, pies_url_free_passwd): New finctions. * lib/libpies.h (strsplit3): New proto. (pies_url_create, pies_url_destroy) (pies_url_get_arg, pies_url_copy) (pies_url_free_user, pies_url_free_passwd) (netrc_scan) (pp_add_option, pp_command_line, mkfilename) (sockaddr_to_str, sockaddr_to_astr): New protos * src/Makefile.am (bin_PROGRAMS): New program: piesctl (pies_SOURCES): Remove addrfmt.c and url.c. (noinst_HEADERS, BUILT_SOURCES): Add piesctl-cl.h * src/cmdline.opt: Use pp_* function family to build preprocessor command line. * src/ctl.c (http_header_hash): Use case-insensitive hashing. (ctlio_finalize_reply): Don't close connection after sending 401 response. (input): Remove ws and wsflags. All uses changed. (input_append): Use strsplit3 to parse the request line. * src/pies.c: Use pp_* function family to build preprocessor command line. Move assert_, mkfilename and _cb+_url functions into libpies. * src/pies.h (pies_sockaddr_storage): Move to libpies.h * src/piesctl.c: New file. * src/piesctl-cl.opt: New file. --- configure.ac | 4 + grecs | 2 +- ident/pam.c | 3 +- lib/Makefile.am | 23 +- lib/addrfmt.c | 174 +++++++++ lib/grecsasrt.c | 78 ++++ lib/grecsasrt.h | 22 ++ lib/libpies.h | 66 +++- lib/mkfilename.c | 42 +++ lib/netrc.c | 217 +++++++++++ lib/pp.c | 71 ++++ lib/split3.c | 83 ++++ lib/url.c | 360 ++++++++++++++++++ src/.gitignore | 2 + src/Makefile.am | 21 +- src/addrfmt.c | 133 ------- src/cmdline.opt | 4 +- src/ctl.c | 43 +-- src/pies.c | 121 +----- src/pies.h | 37 +- src/piesctl-cl.opt | 95 +++++ src/piesctl.c | 1067 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/url.c | 274 -------------- 23 files changed, 2330 insertions(+), 612 deletions(-) create mode 100644 lib/addrfmt.c create mode 100644 lib/grecsasrt.c create mode 100644 lib/grecsasrt.h create mode 100644 lib/mkfilename.c create mode 100644 lib/netrc.c create mode 100644 lib/pp.c create mode 100644 lib/split3.c create mode 100644 lib/url.c delete mode 100644 src/addrfmt.c create mode 100644 src/piesctl-cl.opt create mode 100644 src/piesctl.c delete mode 100644 src/url.c diff --git a/configure.ac b/configure.ac index 1e5ba2a..1ee26ec 100644 --- a/configure.ac +++ b/configure.ac @@ -132,6 +132,10 @@ AH_BOTTOM([ # endif ]) +AC_SUBST([DEFAULT_CONTROL_URL],[unix:///tmp/%s.ctl]) +AC_ARG_VAR([DEFAULT_CONTROL_URL], + [URL of the default control socket]) + IMPRIMATUR_INIT AC_CONFIG_FILES([Makefile diff --git a/grecs b/grecs index 8312f45..3b73967 160000 --- a/grecs +++ b/grecs @@ -1 +1 @@ -Subproject commit 8312f45f48ed9d995a15ee6707257f4c8946528d +Subproject commit 3b73967c62da68d865f32ca91c8407e65b8ddc03 diff --git a/ident/pam.c b/ident/pam.c index b7b9ef4..7302242 100644 --- a/ident/pam.c +++ b/ident/pam.c @@ -36,7 +36,8 @@ struct pam_cred char *s = ptr; \ while (*s) \ *s++ = 0; \ - } while (0) + free (ptr); \ + } while (0) #ifndef PAM_AUTHTOK_RECOVER_ERR # define PAM_AUTHTOK_RECOVER_ERR PAM_CONV_ERR diff --git a/lib/Makefile.am b/lib/Makefile.am index 89812ca..a0d6614 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ # This file is part of GNU Pies. -# Copyright (C) 2005, 2006, 2007, 2008, 2010, 2013 Sergey Poznyakoff +# Copyright (C) 2005-2008, 2010, 2013, 2015 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 @@ -16,15 +16,28 @@ noinst_LIBRARIES=libpies.a -noinst_HEADERS = libpies.h +noinst_HEADERS = libpies.h grecsasrt.h libpies_a_SOURCES=\ + addrfmt.c\ arraymember.c\ + grecsasrt.c\ + mkfilename.c\ + netrc.c\ parsetime.c\ proctitle.c\ - strtotok.c + pp.c\ + split3.c\ + strtotok.c\ + url.c -libpies_a_LIBADD=$(LIBOBJS) +libpies_a_LIBADD=\ + $(LIBOBJS)\ + @GRECS_LDADD@ -AM_CPPFLAGS = -I$(top_srcdir)/gnu -I../gnu +AM_CPPFLAGS = \ + -I$(top_srcdir)/gnu\ + -I../gnu\ + @GRECS_INCLUDES@\ + -DDEFAULT_PREPROCESSOR="$(DEFAULT_PREPROCESSOR)" diff --git a/lib/addrfmt.c b/lib/addrfmt.c new file mode 100644 index 0000000..ff615dd --- /dev/null +++ b/lib/addrfmt.c @@ -0,0 +1,174 @@ +/* This file is part of GNU Pies + Copyright (C) 2009, 2010, 2013, 2015 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "libpies.h" +#include +#include +#include +#include +#include +#include +#include + +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) + +static size_t +format_uint (char **bufptr, size_t *buflen, unsigned n) +{ + char *buf = *bufptr; + size_t len = *buflen; + size_t i; + + if (buf && len == 0) + return 0; + + for (i = 0;;) + { + unsigned x = n % 10; + if (buf) + { + if (len == 1) + break; + *buf++ = x + '0'; + --len; + } + n /= 10; + ++i; + if (n == 0) + break; + } + + if (buf) + { + char *p = *bufptr; + *bufptr = buf; + *buf = 0; + while (--buf > p) + { + char c = *p; + *p++ = *buf; + *buf = c; + } + } + return i; +} + +void +sockaddr_to_str (const struct sockaddr *sa, int salen, + char *bufptr, size_t buflen, + size_t *plen) +{ + 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 += format_uint (&bufptr, &buflen, ntohs (s_in.sin_port)); + 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 += format_uint (&bufptr, &buflen, sa->sa_family); + 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 = malloc (size); + if (!p) + grecs_alloc_die (); + sockaddr_to_str (sa, salen, p, size, NULL); + return p; +} diff --git a/lib/grecsasrt.c b/lib/grecsasrt.c new file mode 100644 index 0000000..29bd0bf --- /dev/null +++ b/lib/grecsasrt.c @@ -0,0 +1,78 @@ +/* This file is part of GNU Pies + Copyright (C) 2015 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include "libpies.h" +#include "grecsasrt.h" + +int +assert_grecs_value_type (grecs_locus_t *locus, + const grecs_value_t *value, int type) +{ + if (GRECS_VALUE_EMPTY_P (value)) + { + grecs_error (locus, 0, _("expected %s"), + grecs_data_type_string (type)); + return 1; + } + if (value->type != type) + { + grecs_error (locus, 0, _("expected %s, but found %s"), + grecs_data_type_string (type), + grecs_data_type_string (value->type)); + return 1; + } + return 0; +} + +int +assert_scalar_stmt (grecs_locus_t *locus, enum grecs_callback_command cmd) +{ + if (cmd != grecs_callback_set_value) + { + grecs_error (locus, 0, _("unexpected block statement")); + return 1; + } + return 0; +} + +int +conf_callback_url (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + struct pies_url *url; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + if (pies_url_create (&url, value->v.string)) + { + grecs_error (locus, 0, _("%s: cannot create URL: %s"), + value->v.string, strerror (errno)); + return 0; + } + if (varptr) + *(struct pies_url **) varptr = url; + else + pies_url_destroy (&url); + return 0; +} + diff --git a/lib/grecsasrt.h b/lib/grecsasrt.h new file mode 100644 index 0000000..bf75731 --- /dev/null +++ b/lib/grecsasrt.h @@ -0,0 +1,22 @@ +/* This file is part of GNU Pies. + Copyright (C) 2009, 2010, 2011, 2013 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 . */ + +int assert_grecs_value_type (grecs_locus_t *locus, + const grecs_value_t *value, int type); +int assert_scalar_stmt (grecs_locus_t *locus, enum grecs_callback_command cmd); +int conf_callback_url (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data); diff --git a/lib/libpies.h b/lib/libpies.h index b9c8af7..dab36df 100644 --- a/lib/libpies.h +++ b/lib/libpies.h @@ -1,5 +1,5 @@ /* This file is part of GNU Pies. - Copyright (C) 2009, 2010, 2011, 2013 Sergey Poznyakoff + Copyright (C) 2009, 2010, 2011, 2013, 2015 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 @@ -14,11 +14,14 @@ You should have received a copy of the GNU General Public License along with GNU Pies. If not, see . */ +#include #include #include #include +#include +#include +#include - #if defined HAVE_SYSCONF && defined _SC_OPEN_MAX # define getmaxfd() sysconf(_SC_OPEN_MAX) #elif defined (HAVE_GETDTABLESIZE) @@ -26,22 +29,12 @@ #else # define getmaxfd() 256 #endif - - - -void *xmalloc (size_t size); -void *xzalloc (size_t size); -void *xcalloc (size_t count, size_t size); -char *xstrdup (const char *str); - void mf_proctitle_init (int argc, char *argv[], char *env[]); void mf_proctitle_format (const char *fmt, ...); - size_t longtostr (long i, char *buf, size_t size); size_t ulongtostr (unsigned long i, char *buf, size_t size); - struct tokendef { @@ -58,4 +51,53 @@ int strtotok_ci (struct tokendef *tab, const char *str, int *pres); int toktostr (struct tokendef *tab, int tok, const char **pres); int is_array_member (char * const * ar, char const *str); +int strsplit3 (const char *input, char *result[3], int flag); + +/* url.c */ +struct pies_url +{ + char *string; + char *scheme; + char *host; + char *port_s; + int port; + char *proto_s; + int proto; + char *path; + char *user; + char *passwd; + int argc; + char **argv; +}; + +int pies_url_create (struct pies_url **purl, const char *str); +void pies_url_destroy (struct pies_url **purl); +const char *pies_url_get_arg (struct pies_url *url, const char *argname); +int pies_url_copy (struct pies_url **purl, struct pies_url *src); +void pies_url_free_user (struct pies_url *url); +void pies_url_free_passwd (struct pies_url *url); + +void netrc_scan (struct pies_url *url); + + +/* pp.c */ +void pp_add_option (const char *opt, const char *arg); +char *pp_command_line (void); + +char *mkfilename (const char *dir, const char *name, const char *suf); + +union pies_sockaddr_storage +{ + struct sockaddr s; + struct sockaddr_in s_in; + struct sockaddr_un s_un; +}; + +/* addrfmt.c */ +void sockaddr_to_str (const struct sockaddr *sa, int salen, + char *bufptr, size_t buflen, + size_t *plen); +char *sockaddr_to_astr (const struct sockaddr *sa, int salen); + + diff --git a/lib/mkfilename.c b/lib/mkfilename.c new file mode 100644 index 0000000..8841fe4 --- /dev/null +++ b/lib/mkfilename.c @@ -0,0 +1,42 @@ +/* This file is part of GNU Pies + Copyright (C) 2015 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "libpies.h" +#include + +char * +mkfilename (const char *dir, const char *name, const char *suf) +{ + size_t dirlen = strlen (dir); + char *s; + + while (dirlen > 0 && dir[dirlen-1] == '/') + dirlen--; + + s = malloc (dirlen + 1 + strlen (name) + (suf ? strlen (suf) : 0) + 1); + if (!s) + grecs_alloc_die (); + strcpy (s, dir); + strcat (s, "/"); + strcat (s, name); + if (suf) + strcat (s, suf); + return s; +} + diff --git a/lib/netrc.c b/lib/netrc.c new file mode 100644 index 0000000..ed8053f --- /dev/null +++ b/lib/netrc.c @@ -0,0 +1,217 @@ +/* This file is part of GNU Pies + Copyright (C) 2015 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "libpies.h" +#include +#include +#include +#include +#include +#include + +/* Compare two hostnames. Return 0 if they have the same address type, + address length *and* at least one of the addresses of A matches + B */ +static int +hostcmp (const char *a, const char *b) +{ + struct hostent *hp = gethostbyname (a); + char **addrlist; + char *dptr; + char **addr; + size_t i, count; + size_t entry_length; + int entry_type; + + if (!hp) + return 1; + + for (count = 1, addr = hp->h_addr_list; *addr; addr++) + count++; + addrlist = grecs_malloc (count * (sizeof *addrlist + hp->h_length) + - hp->h_length); + dptr = (char *) (addrlist + count); + for (i = 0; i < count - 1; i++) + { + memcpy (dptr, hp->h_addr_list[i], hp->h_length); + addrlist[i] = dptr; + dptr += hp->h_length; + } + addrlist[i] = NULL; + entry_length = hp->h_length; + entry_type = hp->h_addrtype; + + hp = gethostbyname (b); + if (!hp || entry_length != hp->h_length || entry_type != hp->h_addrtype) + { + grecs_free (addrlist); + return 1; + } + + for (addr = addrlist; *addr; addr++) + { + char **p; + + for (p = hp->h_addr_list; *p; p++) + { + if (memcmp (*addr, *p, entry_length) == 0) + { + grecs_free (addrlist); + return 0; + } + } + } + grecs_free (addrlist); + return 1; +} + +static int +match_url (size_t argc, char **argv, struct pies_url *url) +{ + if (hostcmp (argv[1], url->host ? url->host : "localhost") == 0) + { + if (argc >= 4 && strcmp (argv[2], "port") == 0) + { + unsigned long n = strtoul (argv[3], NULL, 10); + if (n == url->port) + return 1; + } + else + return 1; + } + return 0; +} + +static void +parse_args (struct grecs_locus *loc, char **argv, + char **username, char **password) +{ + if (*username) + { + grecs_free (*username); + *username = NULL; + } + if (*password) + { + grecs_free (*password); + *password = NULL; + } + + while (*argv) + { + if (!argv[1]) + { + grecs_error (loc, 0, _("incomplete sentence")); + break; + } + if (strcmp (*argv, "login") == 0) + *username = grecs_strdup (argv[1]); + else if (strcmp (*argv, "password") == 0) + *password = grecs_strdup (argv[1]); + argv += 2; + } +} + +/* Parse traditional .netrc file. Set up auth_args fields in accordance with + it. */ +void +netrc_scan_file (FILE *fp, struct grecs_locus *loc, struct pies_url *url) +{ + char *buf = NULL; + size_t n = 0; + struct wordsplit ws; + int wsflags = WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS; + char *username = NULL; + char *password = NULL; + + while (grecs_getline (&buf, &n, fp) > 0) + { + loc->beg.line++; + + if (wordsplit (buf, &ws, wsflags)) + { + grecs_error (loc, 0, "wordsplit: %s", wordsplit_strerror (&ws)); + continue; + } + wsflags |= WRDSF_REUSE; + + if (ws.ws_wordc == 0) + continue; + + if (strcmp (ws.ws_wordv[0], "machine") == 0) + { + if (match_url (ws.ws_wordc, ws.ws_wordv, url)) + { + parse_args (loc, ws.ws_wordv + 2, &username, &password); + break; + } + } + else if (strcmp (ws.ws_wordv[0], "default") == 0) + parse_args (loc, ws.ws_wordv + 1, &username, &password); + else + grecs_error (loc, 0, _("ignoring unrecognized line\n")); + } + grecs_free (buf); + + if (wsflags & WRDSF_REUSE) + wordsplit_free (&ws); + + url->user = username; + url->passwd = password; +} + +void +netrc_scan (struct pies_url *url) +{ + FILE *fp; + struct grecs_locus loc; + char *filename; + char *homedir; + + if (url->user) + return; + + homedir = getenv ("HOME"); + if (!homedir) + { + struct passwd *pwd = getpwuid (getuid ()); + if (!pwd) + return; + homedir = pwd->pw_dir; + } + + filename = mkfilename (homedir, ".netrc", NULL); + fp = fopen (filename, "r"); + if (!fp) + { + if (errno != ENOENT) + grecs_error (NULL, 0, + _("cannot open configuration file %s: %s"), + filename, strerror (errno)); + free (filename); + return; + } + + loc.beg.file = loc.end.file = (char*) filename; + loc.beg.col = loc.end.col = 0; + loc.beg.line = 0; + netrc_scan_file (fp, &loc, url); + fclose (fp); + free (filename); +} diff --git a/lib/pp.c b/lib/pp.c new file mode 100644 index 0000000..9960d7f --- /dev/null +++ b/lib/pp.c @@ -0,0 +1,71 @@ +/* This file is part of GNU Pies. + Copyright (C) 2015 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 +#include +#include + +struct grecs_txtacc *pp_cmd; + +void +pp_add_option (const char *opt, const char *arg) +{ + if (!DEFAULT_PREPROCESSOR) + { + grecs_error (NULL, 0, _("no preprocessor configured")); + exit (EX_CONFIG); + } + if (!pp_cmd) + { + pp_cmd = grecs_txtacc_create (); + grecs_txtacc_grow_string (pp_cmd, DEFAULT_PREPROCESSOR); + } + grecs_txtacc_grow_char (pp_cmd, ' '); + grecs_txtacc_grow_string (pp_cmd, opt); + + if (arg) + { + grecs_txtacc_grow_char (pp_cmd, '\''); + for (; *arg; ++arg) + { + if (*arg == '\'') + grecs_txtacc_grow_string (pp_cmd, "'\\''"); + else + grecs_txtacc_grow (pp_cmd, arg, 1); + } + grecs_txtacc_grow_char (pp_cmd, '\''); + } +} + +char * +pp_command_line (void) +{ + char *ret; + + if (!DEFAULT_PREPROCESSOR) + return NULL; + + if (!pp_cmd) + { + pp_cmd = grecs_txtacc_create (); + grecs_txtacc_grow_string (pp_cmd, DEFAULT_PREPROCESSOR); + } + grecs_txtacc_grow_char (pp_cmd, 0); + ret = grecs_txtacc_finish (pp_cmd, 1); + grecs_txtacc_free (pp_cmd); + pp_cmd = NULL; + return ret; +} diff --git a/lib/split3.c b/lib/split3.c new file mode 100644 index 0000000..ee302e8 --- /dev/null +++ b/lib/split3.c @@ -0,0 +1,83 @@ +/* This file is part of GNU Pies. + Copyright (C) 2007, 2008, 2009, 2010, 2013 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include "libpies.h" + +static int +split3 (const char *input, char *res[3], int flag) +{ + size_t len; + char *p; + + p = strchr (input, ' '); + if (!p) + return 1; + + len = p - input; + res[0] = malloc (len + 1); + if (!res[0]) + return -1; + memcpy (res[0], input, len); + res[0][len] = 0; + + input = p + 1; + + p = (flag ? strrchr : strchr) (input, ' '); + + if (!p) + return 1; + + len = p - input; + res[1] = malloc (len + 1); + if (!res[1]) + return -1; + memcpy (res[1], input, len); + res[1][len] = 0; + + res[2] = strdup (p + 1); + if (!res[2]) + return -1; + + return 0; +} + +int +strsplit3 (const char *input, char *result[3], int flag) +{ + char *tmp[3] = { NULL, NULL, NULL }; + int rc = split3 (input, tmp, flag); + if (rc) + { + int ec = errno; + free (tmp[0]); + free (tmp[1]); + free (tmp[2]); + errno = ec; + } + else + { + result[0] = tmp[0]; + result[1] = tmp[1]; + result[2] = tmp[2]; + } + return 0; +} diff --git a/lib/url.c b/lib/url.c new file mode 100644 index 0000000..41f5c04 --- /dev/null +++ b/lib/url.c @@ -0,0 +1,360 @@ +/* This file is part of GNU Pies + Copyright (C) 2009, 2010, 2013, 2015 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include "libpies.h" +#include + +/* scheme://[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 *start = s + len + 1; + char *q; + unsigned long n = strtoul (start, &q, 10); + if (n > USHRT_MAX) + return 1; + if ((*q && !strchr ("/;", *q))) + { + char *proto = url->proto_s ? url->proto_s : "tcp"; + size_t size = strcspn (start, "/;"); + struct servent *serv; + + alloc_string_len (&url->port_s, start, size); + serv = getservbyname (url->port_s, proto); + if (!serv) + return 1; + url->port = ntohs (serv->s_port); + *str = start + size; + } + else + { + alloc_string_len (&url->port_s, start, q - start); + url->port = n; + *str = q; + } + } + else + *str = s + len; + if (alloc_string_len (&url->host, s, len)) + return 1; + if (**str) + { + if (*(*str)++ == '/') + return url_parse_path (url, str); + else + return url_parse_args (url, str); + } + return 0; +} + +/* On input str points past the mech:// part */ +static int +url_parse_user (struct pies_url *url, char **str) +{ + if (**str == '/') + return url_parse_path (url, str); + else + { + 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_scheme (struct pies_url *url, const char *str) +{ + size_t len; + char *p; + + if (!str) + { + errno = EINVAL; + return 1; + } + + len = strcspn (str, ":+"); + if (!str[len]) + { + errno = EINVAL; + return 1; + } + + alloc_string_len (&url->scheme, str, len); + + str += len; + + if (*str == '+') + { + struct protoent *proto; + + len = strcspn (++str, ":"); + if (str[len] == 0) + { + errno = EINVAL; + return 1; + } + alloc_string_len (&url->proto_s, str, len); + str += len; + proto = getprotobyname (url->proto_s); + if (!proto) + { + errno = EINVAL; + return 1; + } + url->proto = proto->p_proto; + } + else + url->proto = 0; + + /* Skip slashes */ + p = (char*) str + 1; + if (memcmp (p, "//", 2)) + { + errno = EINVAL; + return 1; + } + p += 2; + return url_parse_user (url, &p); +} + +void +pies_url_free_user (struct pies_url *url) +{ + if (url->user) + { + free (url->user); + url->user = NULL; + } +} + +void +pies_url_free_passwd (struct pies_url *url) +{ + if (url->passwd) + { + memset (url->passwd, 0, strlen (url->passwd)); + free (url->passwd); + url->passwd = NULL; + } +} + +void +pies_url_destroy (struct pies_url **purl) +{ + int i; + if (purl && *purl) + { + struct pies_url *url = *purl; + + free (url->string); + free (url->scheme); + free (url->host); + free (url->port_s); + free (url->proto_s); + free (url->path); + pies_url_free_user (url); + pies_url_free_passwd (url); + for (i = 0; i < url->argc; i++) + free (url->argv[i]); + free (url->argv); + free (url); + *purl = NULL; + } +} + +static int +strasgn (char **dst, char *src) +{ + if (!src) + *dst = NULL; + else + { + *dst = strdup (src); + if (!*dst) + return 1; + } + return 0; +} + +static int +do_url_copy (struct pies_url *dest, struct pies_url *src) +{ +#define STRASGN(a,b) if (strasgn (a, b)) return -1; + size_t i; + + STRASGN (&dest->string, src->string); + STRASGN (&dest->scheme, src->scheme); + STRASGN (&dest->host, src->host); + STRASGN (&dest->port_s, src->port_s); + dest->port = src->port; + STRASGN (&dest->proto_s, src->proto_s); + dest->proto = src->proto; + STRASGN (&dest->path, src->path); + STRASGN (&dest->user, src->user); + STRASGN (&dest->passwd, src->passwd); + dest->argv = calloc (src->argc + 1, sizeof (dest->argv[0])); + if (!dest->argv) + return -1; + for (i = 0; i < src->argc; i++) + { + if (!(dest->argv[i] = strdup (src->argv[0]))) + { + dest->argc = i; + return -1; + } + } + dest->argv[i] = NULL; + dest->argc = i; + return 0; +#undef STRASGN +} + +int +pies_url_copy (struct pies_url **purl, struct pies_url *src) +{ + struct pies_url *dest = malloc (sizeof (*dest)); + + if (!dest) + return -1; + memset (dest, 0, sizeof (*dest)); + if (do_url_copy (dest, src)) + { + pies_url_destroy (&dest); + return -1; + } + *purl = dest; + return 0; +} + +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_scheme (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/.gitignore b/src/.gitignore index 4ad4290..a83b9ab 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -6,3 +6,5 @@ meta1gram.output meta1lex.c pies pies.rc +piesctl +piesctl-cl.h diff --git a/src/Makefile.am b/src/Makefile.am index ab4546f..982bd12 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,10 +15,10 @@ # along with GNU Pies. If not, see . */ sbin_PROGRAMS = pies +bin_PROGRAMS = piesctl pies_SOURCES = \ acl.c\ - addrfmt.c\ ctl.c\ depmap.c\ diag.c\ @@ -33,7 +33,6 @@ pies_SOURCES = \ socket.c\ sysdep.c\ sysvinit.c\ - url.c\ utmp.c\ userprivs.c @@ -43,11 +42,12 @@ noinst_HEADERS = \ meta1gram.h\ meta1lex.h\ pies.h\ - prog.h + prog.h\ + piesctl-cl.h meta1lex.c: meta1gram.h -BUILT_SOURCES=cmdline.h +BUILT_SOURCES=cmdline.h piesctl-cl.h incdir=$(pkgdatadir)/$(VERSION)/include inc_DATA = pp-setup @@ -58,8 +58,9 @@ SUFFIXES=.opt .c .h $(AM_V_GEN)m4 -s $(top_srcdir)/@GRECS_SUBDIR@/build-aux/getopt.m4 $< > $@ cmdline.h: cmdline.opt +piesctl-cl.h: piesctl-cl.opt -LDADD = \ +pies_LDADD = \ ../ident/libident.a\ ../lib/libpies.a\ @GRECS_LDADD@\ @@ -67,6 +68,13 @@ LDADD = \ $(MF_PROCTITLE_LIBS)\ @PAM_LIBS@ +piesctl_SOURCES = piesctl.c + +piesctl_LDADD = \ + ../lib/libpies.a\ + @GRECS_LDADD@\ + ../gnu/libgnu.a + pkgstatedir=$(localstatedir)/pies AM_CPPFLAGS=\ @@ -78,7 +86,8 @@ AM_CPPFLAGS=\ -DDEFAULT_PREPROCESSOR="$(DEFAULT_PREPROCESSOR)"\ -DDEFAULT_VERSION_INCLUDE_DIR=\"$(incdir)\"\ -DDEFAULT_INCLUDE_DIR=\"$(pkgdatadir)/include\"\ - -DDEFAULT_STATE_DIR=\"$(pkgstatedir)\" + -DDEFAULT_STATE_DIR=\"$(pkgstatedir)\"\ + -DDEFAULT_CONTROL_URL=\"$(DEFAULT_CONTROL_URL)\" AM_YFLAGS=-dvt -pmeta1 AM_LFLAGS=-dvp -Pmeta1 -olex.yy.c diff --git a/src/addrfmt.c b/src/addrfmt.c deleted file mode 100644 index 59754ae..0000000 --- a/src/addrfmt.c +++ /dev/null @@ -1,133 +0,0 @@ -/* This file is part of GNU Pies - Copyright (C) 2009, 2010, 2013 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 . */ - -#ifdef HAVE_CONFIG_H -# include -#endif -#include "pies.h" -#include -#include -#include -#include -#include -#include -#include - -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/cmdline.opt b/src/cmdline.opt index a0dc703..18791b6 100644 --- a/src/cmdline.opt +++ b/src/cmdline.opt @@ -129,13 +129,13 @@ END OPTION(define,D,[], []) BEGIN - add_pp_option ("-D", optarg); + pp_add_option ("-D", optarg); END OPTION(undefine,U,NAME, []) BEGIN - add_pp_option ("-U", optarg); + pp_add_option ("-U", optarg); END GROUP(Component Management) diff --git a/src/ctl.c b/src/ctl.c index 1da1370..ce13dab 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -21,8 +21,6 @@ #include "base64.h" #include "json.h" -#define DEFAULT_CONTROL_URL "unix:///tmp/%s.ctl" - struct control control; pies_identity_t identity; @@ -171,7 +169,7 @@ static unsigned http_header_hash (void *data, unsigned long n_buckets) { const struct http_header *p = data; - return grecs_hash_string (p->name, n_buckets); + return grecs_hash_string_ci (p->name, n_buckets); } static int @@ -220,9 +218,6 @@ struct input char *input_proto; struct grecs_symtab *headers; size_t input_content_length; - - struct wordsplit ws; - int wsflags; }; static char const * @@ -249,12 +244,6 @@ input_init (struct input *inp) inp->input_uri = NULL; inp->input_proto = NULL; inp->input_content_length = 0; - - inp->ws.ws_delim = " \t()"; - inp->wsflags = WRDSF_DELIM | - WRDSF_NOVAR | WRDSF_NOCMD | - WRDSF_QUOTE | WRDSF_RETURN_DELIMS | - WRDSF_WS; } static void @@ -262,8 +251,6 @@ input_destroy (struct input *inp) { ctlbuf_free (&inp->ibuf); grecs_symtab_free (inp->headers); - if (inp->wsflags & WRDSF_REUSE) - wordsplit_free (&inp->ws); } static void @@ -287,23 +274,23 @@ input_append (struct input *inp, char c) if (c == '\n') { int rc; + char *reqline[3]; ctlbuf_chomp (&inp->ibuf); - rc = wordsplit (inp->ibuf.base, &inp->ws, inp->wsflags); - inp->wsflags |= WRDSF_REUSE; - if (rc) + rc = strsplit3 (inp->ibuf.base, reqline, 1); + if (rc == 0) { - logmsg (LOG_ERR, _("can't parse input line: %s"), - wordsplit_strerror (&inp->ws)); - return 500; + inp->input_method = reqline[0]; + inp->input_uri = reqline[1]; + inp->input_proto = reqline[2]; } - if (inp->ws.ws_wordc == 3) + else if (rc == -1) { - inp->input_method = inp->ws.ws_wordv[0]; - inp->input_uri = inp->ws.ws_wordv[1]; - inp->input_proto = inp->ws.ws_wordv[2]; + logmsg (LOG_ERR, _("can't parse input line: %s"), + strerror (errno)); + return 500; } - else + else { logmsg (LOG_ERR, _("protocol error")); return 400; @@ -379,6 +366,10 @@ input_append (struct input *inp, char c) ctlbuf_write (&inp->ibuf, &c, 1); if (ctlbuf_rdsize (&inp->ibuf) == inp->input_content_length) return 200; + break; + + case is_end: + break; } return 0; } @@ -666,7 +657,7 @@ ctlio_finalize_reply (struct ctlio *io) if (io->state & (CTL_INITIAL_STATE|CTL_AUTHENTICATED_STATE)) { - if (io->code / 100 == 2) + if (io->code / 100 == 2 || io->code == 401) { val = http_get_header (io->input.headers, "connection"); if (val) diff --git a/src/pies.c b/src/pies.c index 7ff226a..5f5d955 100644 --- a/src/pies.c +++ b/src/pies.c @@ -1,5 +1,5 @@ /* This file is part of GNU Pies. - Copyright (C) 2008, 2009, 2010, 2011, 2013 Sergey Poznyakoff + Copyright (C) 2008-2011, 2013-2015 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 @@ -19,6 +19,7 @@ #include #include "meta1lex.h" #include "identity.h" +#include "grecsasrt.h" int preprocess_only; /* Preprocess config, do nothing more */ int lint_mode; /* Test configuration syntax and exit */ @@ -58,9 +59,6 @@ char *mailer_program = "/usr/sbin/sendmail"; char *mailer_command_line = "/usr/sbin/sendmail -oi -t"; int mailer_argc; char **mailer_argv; -struct obstack pp_stk; -struct quoting_options *pp_qopt; - struct config_file { @@ -107,35 +105,6 @@ add_config (enum config_syntax syntax, const char *name) config_list = grecs_list_create (); grecs_list_append (config_list, file); } - - -static void -add_pp_option (const char *opt, const char *arg) -{ - if (!DEFAULT_PREPROCESSOR) - { - logmsg (LOG_ERR, _("no preprocessor configured")); - exit (EX_CONFIG); - } - - obstack_1grow (&pp_stk, ' '); - obstack_grow (&pp_stk, opt, strlen (opt)); - if (arg) - { - char *qarg; - size_t qlen; - - if (!pp_qopt) - { - pp_qopt = clone_quoting_options (NULL); - set_quoting_style (pp_qopt, shell_quoting_style); - } - qarg = quotearg_alloc_mem (arg, strlen (arg), &qlen, pp_qopt); - obstack_grow (&pp_stk, qarg, qlen); - free (qarg); - } -} - /* Logging */ static int @@ -148,39 +117,6 @@ stderr_closed_p () return fd <= 2; } - -int -assert_grecs_value_type (grecs_locus_t *locus, - const grecs_value_t *value, int type) -{ - if (GRECS_VALUE_EMPTY_P (value)) - { - grecs_error (locus, 0, _("expected %s"), - grecs_data_type_string (type)); - return 1; - } - if (value->type != type) - { - grecs_error (locus, 0, _("expected %s, but found %s"), - grecs_data_type_string (type), - grecs_data_type_string (value->type)); - return 1; - } - return 0; -} - -int -assert_scalar_stmt (grecs_locus_t *locus, enum grecs_callback_command cmd) -{ - if (cmd != grecs_callback_set_value) - { - grecs_error (locus, 0, _("unexpected block statement")); - return 1; - } - return 0; -} - - static int _cb_action (enum grecs_callback_command cmd, grecs_locus_t *locus, @@ -770,26 +706,6 @@ _cb_redir (enum grecs_callback_command cmd, return 0; } -static int -_cb_url (enum grecs_callback_command cmd, - grecs_locus_t *locus, - void *varptr, grecs_value_t *value, void *cb_data) -{ - struct pies_url *url; - - if (assert_scalar_stmt (locus, cmd) - || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) - return 1; - if (pies_url_create (&url, value->v.string)) - { - grecs_error (locus, 0, _("%s: cannot create URL: %s"), - value->v.string, strerror (errno)); - return 0; - } - *(struct pies_url **) varptr = url; - return 0; -} - static struct tokendef socktype_xtab[] = { { "stream", SOCK_STREAM }, { "dgram", SOCK_DGRAM }, @@ -1102,7 +1018,7 @@ struct grecs_keyword component_keywords[] = { N_("Listen on the given url."), grecs_type_string, GRECS_DFLT, NULL, offsetof (struct component, socket_url), - _cb_url, + conf_callback_url, }, {"socket-type", /* TRANSLATORS: words after `type:' are keywords. */ @@ -1548,7 +1464,7 @@ struct grecs_keyword control_keywords[] = { N_("url: string"), N_("Listen on the given url."), grecs_type_string, GRECS_DFLT, - &control.url, 0, _cb_url}, + &control.url, 0, conf_callback_url}, {"acl", NULL, N_("Set connection ACL."), @@ -1793,12 +1709,6 @@ config_init () grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR, DEFAULT_INCLUDE_DIR, NULL); grecs_log_to_stderr = log_to_stderr_only; - if (DEFAULT_PREPROCESSOR) - { - obstack_init (&pp_stk); - obstack_grow (&pp_stk, DEFAULT_PREPROCESSOR, - sizeof (DEFAULT_PREPROCESSOR) - 1); - } pies_identity_mechanism_register (&system_identity_mechanism); #ifdef WITH_PAM pies_identity_mechanism_register (&pam_identity_mechanism); @@ -2262,23 +2172,6 @@ set_mailer_argcv () } -static char * -mkfilename (const char *dir, const char *name, const char *suf) -{ - size_t dirlen = strlen (dir); - char *s; - - while (dirlen > 0 && dir[dirlen-1] == '/') - dirlen--; - - s = xmalloc (dirlen + 1 + strlen (name) + strlen (suf) + 1); - strcpy (s, dir); - strcat (s, "/"); - strcat (s, name); - strcat (s, suf); - return s; -} - static void set_conf_file_names (const char *base) { @@ -2316,7 +2209,6 @@ main (int argc, char **argv) #ifdef ENABLE_NLS setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); - bindtextdomain ("pies", LOCALEDIR); textdomain (PACKAGE); #endif mf_proctitle_init (argc, argv, environ); @@ -2407,10 +2299,7 @@ main (int argc, char **argv) if (init_process || !DEFAULT_PREPROCESSOR) grecs_preprocessor = NULL; else - { - grecs_preprocessor = obstack_finish (&pp_stk); - free (pp_qopt); - } + grecs_preprocessor = pp_command_line (); if (preprocess_only) { diff --git a/src/pies.h b/src/pies.h index 56fb72d..afdac66 100644 --- a/src/pies.h +++ b/src/pies.h @@ -1,5 +1,5 @@ /* This file is part of GNU Pies. - Copyright (C) 2008, 2009, 2010, 2011, 2013 Sergey Poznyakoff + Copyright (C) 2008, 2009, 2010, 2011, 2013-2015 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 @@ -262,13 +262,6 @@ struct component }; #define is_sysvinit(cp) ((cp)->mode >= pies_mark_sysvinit || (cp)->runlevels) - -union pies_sockaddr_storage -{ - struct sockaddr s; - struct sockaddr_in s_in; - struct sockaddr_un s_un; -}; enum pies_action { ACTION_CONT, @@ -380,27 +373,6 @@ void component_finish (struct component *comp, grecs_locus_t *locus); struct grecs_keyword *find_component_keyword (const char *ident); -/* url.c */ -struct pies_url -{ - char *string; - char *scheme; - char *host; - char *port_s; - int port; - char *proto_s; - int proto; - char *path; - char *user; - char *passwd; - int argc; - char **argv; -}; - -int pies_url_create (struct pies_url **purl, const char *str); -void pies_url_destroy (struct pies_url **purl); -const char *pies_url_get_arg (struct pies_url *url, const char *argname); - void pies_pause (void); @@ -484,13 +456,6 @@ char *meta_expand_string (const char *string, struct metadef *def, void *data); void meta_free (struct metadef *def); -/* addrfmt.c */ -void sockaddr_to_str (const struct sockaddr *sa, int salen, - char *bufptr, size_t buflen, - size_t *plen); -char *sockaddr_to_astr (const struct sockaddr *sa, int salen); - - /* userprivs.c */ int switch_to_privs (uid_t uid, gid_t gid, struct grecs_list *retain_groups); diff --git a/src/piesctl-cl.opt b/src/piesctl-cl.opt new file mode 100644 index 0000000..04ff051 --- /dev/null +++ b/src/piesctl-cl.opt @@ -0,0 +1,95 @@ +/* This file is part of GNU Pies. -*- c -*- + Copyright (C) 2008-2014 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 . */ + +OPTIONS_BEGIN("piesctl", + [], + [], + [], + [], + []) + +OPTION(instance,i,NAME, + []) +BEGIN + instance = optarg; +END + +OPTION(config-file,c,FILE, + []) +BEGIN + config_file = optarg; +END + +OPTION(config-help,,, + []) +BEGIN + config_help (); + exit (0); +END + +OPTION(,E,, + []) +BEGIN + preprocess_only = 1; +END + +OPTION(verbose,v,, + []) +BEGIN + ++verbose; +END + +OPTION(url,u,URL, + []) +BEGIN + if (pies_url_create (&url, optarg)) + { + grecs_error (NULL, 0, _("%s: cannot create URL: %s"), + optarg, strerror (errno)); + exit (EX_USAGE); + } +END + +GROUP(Preprocessor) + +OPTION(include-directory,I,DIR, + []) +BEGIN + grecs_preproc_add_include_dir (optarg); +END + +OPTION(define,D,[], + []) +BEGIN + pp_add_option ("-D", optarg); +END + +OPTION(undefine,U,NAME, + []) +BEGIN + pp_add_option ("-U", optarg); +END + +OPTIONS_END + +void +parse_options(int argc, char *argv[], int *index) +{ + GETOPT(argc, argv, *index) +} + + + diff --git a/src/piesctl.c b/src/piesctl.c new file mode 100644 index 0000000..0618caa --- /dev/null +++ b/src/piesctl.c @@ -0,0 +1,1067 @@ +/* This file is part of GNU Pies. + Copyright (C) 2015 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "base64.h" +#include "progname.h" +#include "libpies.h" +#include "grecsasrt.h" + +struct pies_url *default_url; /* Control socket URL */ +struct pies_url *url; +char const *instance = "pies"; +char *config_file; +char default_config_file[] = SYSCONFDIR "/piesctl.conf"; +int preprocess_only; +int verbose; + +static void config_help (void); + +#include "piesctl-cl.h" + +static struct grecs_keyword instance_keywords[] = { + {"socket", + N_("url: string"), + N_("Socket URL for that instance."), + grecs_type_string, GRECS_DFLT, + NULL, 0, conf_callback_url}, + { NULL } +}; + +static int +callback_instance (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, void *cb_data) +{ + switch (cmd) + { + case grecs_callback_section_begin: + if (GRECS_VALUE_EMPTY_P (value)) + { + grecs_error (locus, 0, _("missing tag")); + return 1; + } + if (assert_grecs_value_type (&value->locus, value, GRECS_TYPE_STRING)) + return 1; + if (!url && strcmp (value->v.string, instance) == 0) + *(struct pies_url ***) cb_data = &url; + else + *(struct pies_url ***) cb_data = NULL; + break; + + case grecs_callback_section_end: + break; + + case grecs_callback_set_value: + grecs_error (locus, 0, _("invalid use of block statement")); + } + return 0; +} + +struct grecs_keyword piesctl_keywords[] = { + {"socket", + N_("url: string"), + N_("Default socket URL."), + grecs_type_string, GRECS_DFLT, + &default_url, 0, conf_callback_url}, + {"instance", + N_("name"), + N_("Configure connection to a pies instance"), + grecs_type_section, GRECS_DFLT, + NULL, 0, + callback_instance, NULL, instance_keywords }, + { NULL } +}; + +static void +parse_config () +{ + char *file_name; + struct grecs_node *tree; + + grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR, + DEFAULT_INCLUDE_DIR, NULL); + grecs_log_to_stderr = 1; + grecs_preprocessor = pp_command_line (); + + if (config_file) + file_name = config_file; + else if (access (default_config_file, F_OK) == 0) + file_name = default_config_file; + else + file_name = NULL; + + if (file_name) + { + if (preprocess_only) + exit (grecs_preproc_run (file_name, grecs_preprocessor) + ? EX_CONFIG : 0); + if (verbose) + printf ("%s: reading configuration from %s\n", + program_name, file_name); + tree = grecs_parse (file_name); + if (!tree) + exit (EX_CONFIG); + if (grecs_tree_process (tree, piesctl_keywords)) + exit (EX_CONFIG); + if (!url && verbose) + printf ("%s: URL not found in %s\n", + program_name, file_name); + } + + if (!url) + { + /* Try local instance configuration */ + file_name = mkfilename (SYSCONFDIR, instance, ".conf"); + if (access (file_name, F_OK) == 0) + { + struct grecs_node *node; + + if (verbose) + printf ("%s: reading configuration from %s\n", + program_name, file_name); + if (preprocess_only) + exit (grecs_preproc_run (file_name, grecs_preprocessor) + ? EX_CONFIG : 0); + tree = grecs_parse (file_name); + node = grecs_find_node (tree, "/control/socket"); + if (node) + { + assert_grecs_value_type (&node->locus, + node->v.value, GRECS_TYPE_STRING); + if (pies_url_create (&url, node->v.value->v.string)) + { + grecs_error (&node->locus, 0, _("%s: cannot create URL: %s"), + node->v.value->v.string, strerror (errno)); + exit (EX_CONFIG); + } + } + else if (verbose) + printf ("%s: URL not found in %s\n", + program_name, file_name); + } + free (file_name); + } + + if (!url) + { + if (strcmp (instance, "pies") == 0) + { + if (verbose) + printf ("%s: falling back to default URL\n", program_name); + if (default_url) + url = default_url; + else + { + int rc; + size_t len = 0; + file_name = NULL; + grecs_asprintf (&file_name, &len, DEFAULT_CONTROL_URL, instance); + rc = pies_url_create (&url, file_name); + free (file_name); + + if (rc) + { + grecs_error (NULL, 0, _("%s: cannot create URL: %s"), + DEFAULT_CONTROL_URL, strerror (errno)); + exit (EX_SOFTWARE); + } + } + } + else + { + grecs_error (NULL, 0, _("socket name for instance %s not configured"), + instance); + exit (EX_CONFIG); + } + } + if (verbose) + printf ("%s: using URL %s\n", program_name, url->string); +} + +static void +config_help (void) +{ + static char docstring[] = + /* TRANSLATORS: do not translate words between ` and ' */ + N_("Configuration file structure for piesctl.\n" + "For more information, use `info piesctl configuration'."); + grecs_print_docstring (docstring, 0, stdout); + grecs_print_statement_array (piesctl_keywords, 1, 0, stdout); +} + +static void +piesctl_diag(grecs_locus_t const *locus, int err, int errcode, + const char *msg) +{ + fflush (stdout); + fprintf (stderr, "%s: ", program_name); + if (locus) + { + YY_LOCATION_PRINT (stderr, *locus); + fputc(':', stderr); + fputc(' ', stderr); + } + if (!err) + fprintf (stderr, "warning: "); + fprintf (stderr, "%s", msg); + if (errcode) + fprintf (stderr, ": %s", strerror (errno)); + fputc ('\n', stderr); +} + +enum http_method + { + METH_GET, + METH_POST, + METH_DELETE, + METH_PUT, + METH_OPTIONS, + METH_INVALID + }; + +char *method_names[] = { + [METH_GET] = "GET", + [METH_POST] = "POST", + [METH_DELETE] = "DELETE", + [METH_PUT] = "PUT", + [METH_OPTIONS] = "OPTIONS", + NULL +}; + +static char http_version[] = "HTTP/1.1"; + +struct shttp_io +{ + int code; + struct grecs_symtab *headers; + unsigned long content_length; + char *content; +}; + +struct shttp_buf +{ + char *base; + size_t size; +}; + +struct shttp_connection +{ + FILE *fp; + struct pies_url *url; + struct shttp_io req; + struct shttp_io resp; + struct shttp_buf sendbuf; + struct shttp_buf readbuf; + char *b64auth; + char *status_line[3]; + struct json_value *result; +}; + +struct http_header +{ + char *name; + char *value; +}; + +static unsigned +http_header_hash (void *data, unsigned long n_buckets) +{ + const struct http_header *p = data; + return grecs_hash_string_ci (p->name, n_buckets); +} + +static int +http_header_compare (void const *data1, void const *data2) +{ + const struct http_header *p1 = data1; + const struct http_header *p2 = data2; + return strcasecmp (p1->name, p2->name); +} + +static int +http_header_copy (void *a, void *b) +{ + struct http_header *dst = a; + struct http_header *src = b; + + dst->name = grecs_strdup (src->name); + dst->value = src->value ? grecs_strdup (src->value) : NULL; + + return 0; +} + +static void +http_header_free (void *p) +{ + const struct http_header *h = p; + grecs_free (h->name); + grecs_free (h->value); + free (p); +} + +static void +shttp_io_init (struct shttp_io *io) +{ + io->code = 0; + io->content_length = 0; + grecs_free (io->content); + io->content = NULL; + grecs_symtab_clear (io->headers); +} + +static int +shttp_io_header_put (struct shttp_io *io, char const *name, + char const *fmt, ...) +{ + int install = 1; + struct http_header key, *ret; + va_list ap; + size_t len = 0; + + if (!io->headers) + io->headers = grecs_symtab_create (sizeof (struct http_header), + http_header_hash, + http_header_compare, + http_header_copy, + NULL, + http_header_free); + key.name = (char *) name; + key.value = NULL; + ret = grecs_symtab_lookup_or_install (io->headers, &key, &install); + if (!ret) + { + grecs_error (NULL, 0, _("cannot install header %s: %s"), + name, strerror (errno)); + return 1; + } + if (!install) + grecs_free (ret->value); + va_start (ap, fmt); + grecs_vasprintf (&ret->value, &len, fmt, ap); + va_end (ap); + return 0; +} + +static char * +shttp_io_header_get (struct shttp_io *io, char const *name) +{ + struct http_header key, *ret; + + if (!io->headers) + return NULL; + key.name = (char*) name; + ret = grecs_symtab_lookup_or_install (io->headers, &key, NULL); + return ret ? ret->value : NULL; +} + +static void +shttp_io_free (struct shttp_io *io) +{ + grecs_symtab_free (io->headers); + grecs_free (io->content); +} + +static struct shttp_connection * +shttp_connect (struct pies_url *url) +{ + int fd; + union pies_sockaddr_storage addr; + socklen_t socklen; + int flags; + struct shttp_connection *conn; + FILE *fp; + + if (strcmp (url->scheme, "unix") == 0 + || strcmp (url->scheme, "file") == 0 + || strcmp (url->scheme, "socket") == 0) + { + struct stat st; + + if (url->port) + { + grecs_error (NULL, 0, _("%s: invalid connection type: " + "port is meaningless for UNIX sockets"), + url->string); + return NULL; + } + + if (strlen (url->path) > sizeof addr.s_un.sun_path) + { + errno = EINVAL; + grecs_error (NULL, 0, + _("%s: UNIX socket name too long"), + url->path); + return NULL; + } + + addr.s.sa_family = PF_UNIX; + socklen = sizeof (addr.s_un); + strcpy (addr.s_un.sun_path, url->path); + + if (stat (url->path, &st)) + { + if (errno != ENOENT) + { + grecs_error (NULL, 0, + _("%s: cannot stat socket: %s"), + url->path, + strerror (errno)); + return NULL; + } + } + else + { + if (!S_ISSOCK (st.st_mode)) + { + grecs_error (NULL, 0, + _("%s: not a socket"), + url->path); + return NULL; + } + } + } + else if (strcmp (url->scheme, "inet") == 0) + { + short pnum; + struct hostent *hp; + + addr.s_in.sin_family = PF_INET; + socklen = sizeof (addr.s_in); + + pnum = url->port ? url->port : 8080; + + hp = gethostbyname (url->host); + if (!hp) + { + grecs_error (NULL, 0, _("%s: unknown host name"), url->string); + return NULL; + } + + addr.s_in.sin_family = hp->h_addrtype; + switch (hp->h_addrtype) + { + case AF_INET: + memmove (&addr.s_in.sin_addr, hp->h_addr, 4); + addr.s_in.sin_port = htons (pnum); + break; + + default: + grecs_error (NULL, 0, + _("%s: invalid connection type: " + "unsupported address family"), + url->string); + return NULL; + } + } + else + { + grecs_error (NULL, 0, _("%s: unsupported protocol"), + url->string); + return NULL; + } + + fd = socket (addr.s.sa_family, SOCK_STREAM, 0); + if (fd == -1) + { + grecs_error (NULL, 0, _("%s: unable to create new socket: %s"), + url->string, strerror(errno)); + return NULL; + } + + if ((flags = fcntl (fd, F_GETFD, 0)) == -1 + || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) + grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"), + url->string, strerror (errno)); + + // FIXME: Bind to local address + + if (connect (fd, &addr.s, socklen)) + { + grecs_error (NULL, 0, _("%s: cannot connect: %s"), + url->string, strerror (errno)); + close (fd); + return NULL; + } + + fp = fdopen (fd, "w+"); + if (!fp) + { + grecs_error (NULL, 0, "fdopen: %s", strerror (errno)); + close (fd); + return NULL; + } + + conn = grecs_zalloc (sizeof (*conn)); + conn->fp = fp; + if (pies_url_copy (&conn->url, url)) + grecs_alloc_die (); + + netrc_scan (conn->url); + + return conn; +} + +static void +shttp_auth_free (struct shttp_connection *conn) +{ + if (conn->b64auth) + { + memset (conn->b64auth, 0, strlen (conn->b64auth)); + free (conn->b64auth); + conn->b64auth = NULL; + } +} + +static void +shttp_status_line_free (struct shttp_connection *conn) +{ + free (conn->status_line[0]); + free (conn->status_line[1]); + free (conn->status_line[2]); + conn->status_line[0] = NULL; + conn->status_line[1] = NULL; + conn->status_line[2] = NULL; +} + +static void +shttp_close (struct shttp_connection *conn) +{ + fclose (conn->fp); + pies_url_destroy (&conn->url); + shttp_io_free (&conn->req); + shttp_io_free (&conn->resp); + grecs_fre