aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2015-12-31 13:59:18 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2015-12-31 15:58:17 +0200
commit00e6c3c3ed06a258a02943fc49fa7c528025d747 (patch)
tree52530660be48fb5f4611bcc9393886bab8a70b72 /src
parent7f204cc788de3e03a51087b1273deb5b59288cf2 (diff)
downloadpies-00e6c3c3ed06a258a02943fc49fa7c528025d747.tar.gz
pies-00e6c3c3ed06a258a02943fc49fa7c528025d747.tar.bz2
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.
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore2
-rw-r--r--src/Makefile.am21
-rw-r--r--src/addrfmt.c133
-rw-r--r--src/cmdline.opt4
-rw-r--r--src/ctl.c43
-rw-r--r--src/pies.c121
-rw-r--r--src/pies.h37
-rw-r--r--src/piesctl-cl.opt95
-rw-r--r--src/piesctl.c1067
-rw-r--r--src/url.c274
10 files changed, 1204 insertions, 593 deletions
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 <http://www.gnu.org/licenses/>. */
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 <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/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,[<NAME[=VALUE]>],
[<define a preprocessor symbol NAME as having VALUE or empty>])
BEGIN
- add_pp_option ("-D", optarg);
+ pp_add_option ("-D", optarg);
END
OPTION(undefine,U,NAME,
[<undefine a preprocessor symbol 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 <configmake.h>
#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 <http://www.gnu.org/licenses/>. */
+
+OPTIONS_BEGIN("piesctl",
+ [<GNU pies control program>],
+ [<COMMAND [ARG...]>],
+ [<gnu>],
+ [<copyright_year=2008-2015>],
+ [<copyright_holder=Sergey Poznyakoff>])
+
+OPTION(instance,i,NAME,
+ [<connect to instance NAME>])
+BEGIN
+ instance = optarg;
+END
+
+OPTION(config-file,c,FILE,
+ [<use FILE instead of the default configuration>])
+BEGIN
+ config_file = optarg;
+END
+
+OPTION(config-help,,,
+ [<show configuration file summary>])
+BEGIN
+ config_help ();
+ exit (0);
+END
+
+OPTION(,E,,
+ [<preprocess config and exit>])
+BEGIN
+ preprocess_only = 1;
+END
+
+OPTION(verbose,v,,
+ [<verbose diagnostics>])
+BEGIN
+ ++verbose;
+END
+
+OPTION(url,u,URL,
+ [<connect to this socket>])
+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,
+ [<add include directory>])
+BEGIN
+ grecs_preproc_add_include_dir (optarg);
+END
+
+OPTION(define,D,[<NAME[=VALUE]>],
+ [<define a preprocessor symbol NAME as having VALUE or empty>])
+BEGIN
+ pp_add_option ("-D", optarg);
+END
+
+OPTION(undefine,U,NAME,
+ [<undefine a preprocessor symbol 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <configmake.h>
+#include <grecs.h>
+#include <grecs-locus.h>
+#include <json.h>
+#include <wordsplit.h>
+#include <sysexits.h>
+#include <termios.h>
+#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);
+}
+
+sta