summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2015-12-31 11:59:18 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2015-12-31 13:58:17 (GMT)
commit00e6c3c3ed06a258a02943fc49fa7c528025d747 (patch) (side-by-side diff)
tree52530660be48fb5f4611bcc9393886bab8a70b72
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 (more/less context) (ignore whitespace changes)
-rw-r--r--configure.ac4
m---------grecs0
-rw-r--r--ident/pam.c3
-rw-r--r--lib/Makefile.am23
-rw-r--r--lib/addrfmt.c (renamed from src/addrfmt.c)63
-rw-r--r--lib/grecsasrt.c78
-rw-r--r--lib/grecsasrt.h22
-rw-r--r--lib/libpies.h66
-rw-r--r--lib/mkfilename.c42
-rw-r--r--lib/netrc.c217
-rw-r--r--lib/pp.c71
-rw-r--r--lib/split3.c83
-rw-r--r--lib/url.c (renamed from src/url.c)94
-rw-r--r--src/.gitignore2
-rw-r--r--src/Makefile.am21
-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
21 files changed, 1937 insertions, 219 deletions
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
-Subproject 8312f45f48ed9d995a15ee6707257f4c8946528
+Subproject 3b73967c62da68d865f32ca91c8407e65b8ddc0
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/src/addrfmt.c b/lib/addrfmt.c
index 59754ae..ff615dd 100644
--- a/src/addrfmt.c
+++ b/lib/addrfmt.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2009, 2010, 2013 Sergey Poznyakoff
+ 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
@@ -17,7 +17,7 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
-#include "pies.h"
+#include "libpies.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
@@ -37,7 +37,7 @@ str2port (char *str)
serv = getservbyname (str, "tcp");
if (serv != NULL)
- port = ntohs(serv->s_port);
+ port = ntohs (serv->s_port);
else
{
unsigned long l;
@@ -79,22 +79,61 @@ _my_stpcpy (char **pbuf, size_t *psize, const char *src)
#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)
{
- 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, inet_ntoa (s_in.sin_addr));
len += _my_stpcpy (&bufptr, &buflen, ":");
- len += _my_stpcpy (&bufptr, &buflen,
- umaxtostr(ntohs (s_in.sin_port), buf));
+ len += format_uint (&bufptr, &buflen, ntohs (s_in.sin_port));
break;
}
@@ -113,7 +152,7 @@ sockaddr_to_str (const struct sockaddr *sa, int salen,
default:
len += _my_stpcpy (&bufptr, &buflen, "{Unsupported family: ");
- len += _my_stpcpy (&bufptr, &buflen, umaxtostr (sa->sa_family, buf));
+ len += format_uint (&bufptr, &buflen, sa->sa_family);
len += _my_stpcpy (&bufptr, &buflen, "}");
}
if (plen)
@@ -126,8 +165,10 @@ 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);
+ 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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <errno.h>
+#include <grecs.h>
+#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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+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 <http://www.gnu.org/licenses/>. */
+#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <gettext.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <grecs.h>
-
#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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "libpies.h"
+#include <grecs.h>
+
+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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "libpies.h"
+#include <unistd.h>
+#include <pwd.h>
+#include <grecs.h>
+#include <wordsplit.h>
+#include <errno.h>
+#include <netdb.h>
+
+/* 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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <grecs.h>
+#include <sysexits.h>
+
+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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#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/src/url.c b/lib/url.c
index a02a41f..41f5c04 100644
--- a/src/url.c
+++ b/lib/url.c
@@ -1,5 +1,5 @@
/* This file is part of GNU Pies
- Copyright (C) 2009, 2010, 2013 Sergey Poznyakoff
+ 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
@@ -17,11 +17,12 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
-#include "pies.h"
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
+#include "libpies.h"
+#include <wordsplit.h>
/* scheme://[user[:password]@][host[:port]/]path[;arg=str[;arg=str...]
*/
@@ -215,6 +216,27 @@ url_parse_scheme (struct pies_url *url, const char *str)
}
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;
@@ -228,8 +250,8 @@ pies_url_destroy (struct pies_url **purl)
free (url->port_s);
free (url->proto_s);
free (url->path);
- free (url->user);
- free (url->passwd);
+ pies_url_free_user (url);
+ pies_url_free_passwd (url);
for (i = 0; i < url->argc; i++)
free (url->argv[i]);
free (url->argv);
@@ -238,6 +260,70 @@ pies_url_destroy (struct pies_url **purl)
}
}
+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)
{
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/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
--- a/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
--- a/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);
+}
+
+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_free (conn->sendbuf.base);
+ grecs_free (conn->readbuf.base);
+ shttp_status_line_free (conn);
+ shttp_auth_free (conn);
+ json_value_free (conn->result);
+ grecs_free (conn);
+}
+
+static void
+shttp_send_line (struct shttp_connection *conn, char const *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ grecs_vasprintf (&conn->sendbuf.base, &conn->sendbuf.size, fmt, ap);
+ va_end (ap);
+ if (verbose > 1)
+ printf ("> %s\n", conn->sendbuf.base);
+ fprintf (conn->fp, "%s\r\n", conn->sendbuf.base);
+}
+
+static int
+shttp_read_line (struct shttp_connection *conn)
+{
+ ssize_t rc;
+ rc = grecs_getline (&conn->readbuf.base, &conn->readbuf.size, conn->fp);
+ if (rc > 0)
+ {
+ if (conn->readbuf.base[--rc] == '\n')
+ {
+ if (rc > 0 && conn->readbuf.base[rc-1] == '\r')
+ --rc;
+ conn->readbuf.base[rc] = 0;
+ }
+ if (verbose > 1)
+ printf ("< %s\n", conn->readbuf.base);
+ return 0;
+ }
+ else if (rc == 0)
+ {
+ if (verbose > 1)
+ printf ("<EOF>\n");
+ return 1;
+ }
+ grecs_error (NULL, errno, _("HTTP read error"));
+ exit (EX_UNAVAILABLE);
+}
+
+static int
+send_header (void *sym, void *data)
+{
+ struct http_header *h = sym;
+ struct shttp_connection *conn = data;
+ shttp_send_line (conn, "%s: %s", h->name, h->value);
+ return 0;
+}
+
+static void
+shttp_request_send (struct shttp_connection *conn, int method, char const *uri)
+{
+ shttp_io_init (&conn->req);
+ shttp_send_line (conn, "%s %s %s", method_names[method], uri, http_version);
+ shttp_send_line (conn, "Host: %s",
+ conn->url->host ? conn->url->host : "localhost");
+ if (conn->url->user)
+ {
+ shttp_auth_free (conn);
+
+ grecs_asprintf (&conn->readbuf.base, &conn->readbuf.size, "%s:%s",
+ conn->url->user,
+ conn->url->passwd ? conn->url->passwd : 0);
+ base64_encode_alloc (conn->readbuf.base, strlen (conn->readbuf.base),
+ &conn->b64auth);
+
+ pies_url_free_user (conn->url);
+ pies_url_free_passwd (conn->url);
+ }
+
+ if (conn->b64auth)
+ shttp_send_line (conn, "Authorization: Basic %s", conn->b64auth);
+
+ grecs_symtab_enumerate (conn->req.headers, send_header, conn);
+ if (conn->req.content_length)
+ {
+ shttp_send_line (conn, "Content-Length: %lu", conn->req.content_length);
+ shttp_send_line (conn, "Content-Type: application/json");
+ }
+ shttp_send_line (conn, "");
+ if (conn->req.content_length)
+ {
+ size_t s = fwrite (conn->req.content, 1, conn->req.content_length,
+ conn->fp);
+ if (s != conn->req.content_length)
+ {
+ grecs_error (NULL, 0, _("wrote %lu of %lu bytes of content"),
+ (unsigned long) s, conn->req.content_length);
+ exit (EX_UNAVAILABLE);
+ }
+ }
+}
+
+#define ISWS(c) ((c) == ' ' || (c) == '\t')
+
+static void
+shttp_save_header (struct shttp_connection *conn)
+{
+ char *p;
+
+ for (p = conn->readbuf.base; *p; p++)
+ if (*p == ':')
+ {
+ *p++ = 0;
+ p += strspn (p, " \t");
+ shttp_io_header_put (&conn->resp, conn->readbuf.base, "%s", p);
+ return;
+ }
+
+ grecs_error (NULL, 0, _("malformed header line: %s"),
+ conn->readbuf.base);
+ exit (EX_UNAVAILABLE);
+}
+
+static void
+shttp_get_reply (struct shttp_connection *conn)
+{
+ enum input_state { is_initial, is_headers, is_content } state = is_initial;
+ char const *val;
+
+ shttp_io_init (&conn->resp);
+ shttp_status_line_free (conn);
+ json_value_free (conn->result);
+ conn->result = NULL;
+
+ while (state != is_content)
+ {
+ if (shttp_read_line (conn))
+ break;
+ if (state == is_initial)
+ {
+ unsigned long n;
+ char *p;
+ int rc;
+
+ rc = strsplit3 (conn->readbuf.base, conn->status_line, 0);
+ if (rc == -1)
+ {
+ grecs_error (NULL, errno, "strsplit3");
+ exit (EX_OSERR);
+ }
+ else if (rc == 1)
+ {
+ grecs_error (NULL, 0, _("unexpected reply: %s"),
+ conn->readbuf.base);
+ exit (EX_UNAVAILABLE);
+ }
+
+ if (strcmp (conn->status_line[0], http_version))
+ {
+ grecs_error (NULL, 0, _("unsupported HTTP version: %s"),
+ conn->status_line[0]);
+ exit (EX_UNAVAILABLE);
+ }
+ n = strtoul (conn->status_line[1], &p, 0);
+ if (*p || n > 599)
+ {
+ grecs_error (NULL, 0, _("bad reply status: %s"),
+ conn->readbuf.base);
+ exit (EX_UNAVAILABLE);
+ }
+ conn->resp.code = n;
+
+ state = is_headers;
+ }
+ else if (state == is_headers)
+ {
+ if (conn->readbuf.base[0])
+ shttp_save_header (conn);
+ else
+ state = is_content;
+ }
+ }
+
+ val = shttp_io_header_get (&conn->resp, "content-length");
+ if (val)
+ {
+ ssize_t rc;
+ char *p;
+ unsigned long len = strtoul (val, &p, 10);
+
+ if (*p)
+ {
+ grecs_error (NULL, 0, _("bad content length: %s"), val);
+ exit (EX_UNAVAILABLE);
+ }
+
+ if (len)
+ {
+ conn->resp.content = grecs_malloc (len + 1);
+ conn->resp.content_length = len;
+ rc = fread (conn->resp.content, len, 1, conn->fp);
+ if (rc != 1)
+ {
+ grecs_error (NULL, ferror (conn->fp) ? errno : 0,
+ _("content read error"));
+ exit (EX_UNAVAILABLE);
+ }
+
+ val = shttp_io_header_get (&conn->resp, "content-type");
+ if (!val)
+ grecs_error (NULL, 0, _("no content type specified"));
+ else if (strcmp (val, "application/json"))
+ grecs_error (NULL, 0, _("unsupported content type: %s"), val);
+ else
+ {
+ conn->result = json_parse_string (conn->resp.content,
+ conn->resp.content_length);
+ if (!conn->result)
+ {
+ grecs_error (&json_err_locus, 0,
+ "%s", json_err_diag);
+ grecs_error (NULL, 0, _("original json was: %s"),
+ conn->resp.content);
+ grecs_error (NULL, 0, _("original status line: %s %s %s"),
+ conn->status_line[0],
+ conn->status_line[1],
+ conn->status_line[2]);
+ exit (EX_UNAVAILABLE);
+ }
+ }
+ }
+ }
+}
+
+static void
+stderr_writer (void *closure, char const *text, size_t len)
+{
+ fwrite (text, len, 1, stderr);
+}
+
+static void
+shttp_format_result (struct shttp_connection *conn)
+{
+ struct json_format fmt = {
+ .indent = 2,
+ .precision = 0,
+ .write = stderr_writer,
+ };
+ fprintf (stderr, "%s: raw JSON reply follows:\n", program_name);
+ json_format_value (conn->result, &fmt);
+}
+
+static void
+shttp_fatal (struct shttp_connection *conn)
+{
+ struct json_value *jv;
+
+ if (conn->result && (jv = json_value_lookup (conn->result, "error_message")))
+ {
+ if (jv->type == json_string)
+ grecs_error (NULL, 0, "%s", jv->v.s);
+ else
+ shttp_format_result (conn);
+ }
+ else
+ grecs_error (NULL, 0, "%s", conn->status_line[2]);
+
+ exit (1);
+}
+
+static void
+echo_off (struct termios *stored_settings)
+{
+ struct termios new_settings;
+ tcgetattr (0, stored_settings);
+ new_settings = *stored_settings;
+ new_settings.c_lflag &= (~ECHO);
+ tcsetattr (0, TCSANOW, &new_settings);
+}
+
+static void
+echo_on (struct termios *stored_settings)
+{
+ tcsetattr (0, TCSANOW, stored_settings);
+}
+
+static char *
+getans (int echo, const char *fmt, ...)
+{
+ struct termios stored_settings;
+ char *reply = NULL;
+ size_t reply_len = 0;
+ size_t len;
+ va_list ap;
+
+ va_start (ap, fmt);
+ vprintf (fmt, ap);
+ va_end (ap);
+ printf (": ");
+ fflush (stdout);
+ if (!echo)
+ echo_off (&stored_settings);
+ grecs_getline (&reply, &reply_len, stdin);
+ if (!echo)
+ {
+ echo_on (&stored_settings);
+ putchar ('\n');
+ fflush (stdout);
+ }
+ len = strlen (reply);
+ if (len)
+ reply[len - 1] = 0;
+ return reply;
+}
+
+static void
+shttp_get_credentials (struct shttp_connection *conn)
+{
+ char const *val = shttp_io_header_get (&conn->resp, "WWW-Authenticate");
+ char *realm = NULL;
+
+ if (val)
+ {
+ struct wordsplit ws;
+ if (wordsplit (val, &ws,
+ WRDSF_NOVAR | WRDSF_NOCMD
+ | WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS))
+ grecs_error (NULL, 0, "wordsplit: %s", wordsplit_strerror (&ws));
+ else
+ {
+ if (ws.ws_wordc > 0)
+ {
+ size_t i;
+
+ if (strcmp (ws.ws_wordv[0], "Basic"))
+ {
+ grecs_error (NULL, 0,
+ _("unsupported authentication type: %s"),
+ ws.ws_wordv[0]);
+ shttp_fatal (conn);
+ }
+ for (i = 1; i < ws.ws_wordc; i++)
+ if (strncmp (ws.ws_wordv[i], "realm=", 6) == 0)
+ {
+ realm = grecs_strdup (ws.ws_wordv[i] + 6);
+ break;
+ }
+ }
+ wordsplit_free (&ws);
+ }
+ }
+
+ if (!realm)
+ realm = grecs_strdup ("Server");
+
+ conn->url->user = getans (1, "%s user", realm);
+ conn->url->passwd = getans (0, "%s password", realm);
+
+ grecs_free (realm);
+}
+
+static void
+shttp_process (struct shttp_connection *conn, int method, char const *uri)
+{
+ for (;;)
+ {
+ shttp_request_send (conn, method, uri);
+ shttp_get_reply (conn);
+ if (conn->resp.code / 100 == 2)
+ return;
+ if (conn->resp.code == 401 && isatty (fileno (stdin)))
+ shttp_get_credentials (conn);
+ else
+ shttp_fatal (conn);
+ }
+}
+
+static int
+com_list (struct shttp_connection *conn, int argc, char **argv)
+{
+ shttp_process (conn, METH_GET, "/programs");
+ return 0;
+}
+
+static int
+com_stop (struct shttp_connection *conn, int argc, char **argv)
+{
+ abort ();
+ return 0;
+}
+
+static int
+com_start (struct shttp_connection *conn, int argc, char **argv)
+{
+ abort ();
+ return 0;
+}
+
+static int
+com_restart (struct shttp_connection *conn, int argc, char **argv)
+{
+ abort ();
+ return 0;
+}
+
+static int
+com_shutdown (struct shttp_connection *conn, int argc, char **argv)
+{
+ abort ();
+ return 0;
+}
+
+static int
+com_reboot (struct shttp_connection *conn, int argc, char **argv)
+{
+ abort ();
+ return 0;
+}
+
+typedef int (*ctlcom_t) (struct shttp_connection *, int, char **);
+
+struct comtab
+{
+ char const *name;
+ ctlcom_t command;
+};
+
+static struct comtab comtab[] = {
+ { "list", com_list },
+ { "stop", com_stop },
+ { "start", com_start },
+ { "restart", com_restart },
+ { "shutdown", com_shutdown },
+ { "reboot", com_reboot },
+ { NULL }
+};
+
+static struct comtab *
+find_command (char const *name)
+{
+ struct comtab *p, *match = NULL;
+ size_t match_count = 0;
+ size_t len = strlen (name);
+
+ for (p = comtab; p->name; p++)
+ if (len <= strlen (p->name)
+ && memcmp (p->name, name, len) == 0)
+ {
+ if (!match)
+ {
+ match = p;
+ ++match_count;
+ }
+ else
+ {
+ if (match_count == 1)
+ fprintf (stderr, _("%s: ambiguous command %s:\n"),
+ program_name, name);
+ ++match_count;
+ fprintf (stderr, " %s\n", p->name);
+ }
+ }
+
+ if (match_count == 0)
+ {
+ grecs_error (NULL, 0, _("unknown command: %s"), name);
+ exit (EX_USAGE);
+ }
+ else if (match_count > 1)
+ exit (EX_USAGE);
+
+ return match;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int i, rc;
+ struct shttp_connection *conn;
+ struct comtab *cmd;
+
+ set_program_name (argv[0]);
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
+ grecs_print_diag_fun = piesctl_diag;
+
+ parse_options (argc, argv, &i);
+ argc -= i;
+ argv += i;
+
+ parse_config ();
+
+ conn = shttp_connect (url);
+ if (!conn)
+ return EX_UNAVAILABLE;
+
+ cmd = find_command (argv[0]);
+ argv[0] = (char*) cmd->name;
+ rc = cmd->command (conn, argc, argv);
+ shttp_close (conn);
+ return rc;
+}

Return to:

Send suggestions and report system problems to the System administrator.