diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-12-31 13:59:18 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-12-31 15:58:17 +0200 |
commit | 00e6c3c3ed06a258a02943fc49fa7c528025d747 (patch) | |
tree | 52530660be48fb5f4611bcc9393886bab8a70b72 /src | |
parent | 7f204cc788de3e03a51087b1273deb5b59288cf2 (diff) | |
download | pies-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/.gitignore | 2 | ||||
-rw-r--r-- | src/Makefile.am | 21 | ||||
-rw-r--r-- | src/addrfmt.c | 133 | ||||
-rw-r--r-- | src/cmdline.opt | 4 | ||||
-rw-r--r-- | src/ctl.c | 43 | ||||
-rw-r--r-- | src/pies.c | 121 | ||||
-rw-r--r-- | src/pies.h | 37 | ||||
-rw-r--r-- | src/piesctl-cl.opt | 95 | ||||
-rw-r--r-- | src/piesctl.c | 1067 | ||||
-rw-r--r-- | src/url.c | 274 |
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) @@ -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) @@ -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) { @@ -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 |