aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-08 00:23:38 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2011-05-09 13:29:28 +0300
commitf85feb9fca07842eead909b83dd55d6a07392b7d (patch)
tree70d2a123aeba7e5ef59a47aec4187e9d31969b5d
parent5dd4d8742941e15967dec29e84756485986b7e36 (diff)
downloadjoh-f85feb9fca07842eead909b83dd55d6a07392b7d.tar.gz
joh-f85feb9fca07842eead909b83dd55d6a07392b7d.tar.bz2
Add configuration file and long option support (using Grecs).
* configure.ac: Check for getopt.h and getopt_long. Call GRECS_SETUP * Makefile.am (ACLOCAL_AMFLAGS): Update (SUBDIRS): Add grecs. (ChangeLog): Use git2chg.awk from grecs. * src/.gitignore: Add cmdline.h. * src/Makefile.am (EXTRA_DIST): Add cmdline.opt (BUILT_SOURCES): Add cmdline.h (libjoh_a_SOURCES): Add cmdline.h, config.c and hintab.c (LDADD): Link with libgrecs. (AM_CPPFLAGS): New variable. * src/error.c: Rewrite using debug categories. * src/joh.h: Include grecs.h. Revamp debugging support. All uses updated. (joh_sockaddr_hints) <name>: New member. * src/johd.c: Add configuration file support. * git2chg.awk: Remove. * src/cmdline.opt: New file. * src/hintab.c: New file. * src/config.c: New file.
-rw-r--r--.gitmodules3
-rw-r--r--Makefile.am6
-rwxr-xr-xbootstrap5
-rw-r--r--configure.ac9
-rw-r--r--doc/johd.82
-rw-r--r--git2chg.awk45
m---------grecs0
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am19
-rw-r--r--src/cmdline.opt262
-rw-r--r--src/config.c461
-rw-r--r--src/error.c123
-rw-r--r--src/hintab.c90
-rw-r--r--src/http.c25
-rw-r--r--src/joh.h50
-rw-r--r--src/johcgi.c3
-rw-r--r--src/johd.c326
-rw-r--r--src/key.c3
-rw-r--r--src/pdubuf.c3
-rw-r--r--src/selloop.c19
-rw-r--r--src/sess.c16
-rw-r--r--src/socket.c16
-rw-r--r--src/transparent.c9
23 files changed, 1112 insertions, 384 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..cfea5f7
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "grecs"]
+ path = grecs
+ url = ssh://git.gnu.org.ua/gitroot/grecs.git
diff --git a/Makefile.am b/Makefile.am
index 89beb58..f93556f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,11 +14,13 @@
# You should have received a copy of the GNU General Public License
# along with JOH. If not, see <http://www.gnu.org/licenses/>.
-SUBDIRS = src doc
+ACLOCAL_AMFLAGS = -I grecs/am
+
+SUBDIRS = grecs src doc
.PHONY: ChangeLog
ChangeLog:
$(AM_V_GEN)if test -d .git; then \
git log --pretty='format:%ct %an <%ae>%n%n%s%n%n%b%n' | \
- awk -f $(top_srcdir)/git2chg.awk > ChangeLog; \
+ awk -f $(top_srcdir)/grecs/build-aux/git2chg.awk > ChangeLog; \
fi
diff --git a/bootstrap b/bootstrap
index 052b8b9..449a307 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,8 +1,11 @@
# !/bin/sh
+set -e
+git submodule init
+git submodule update
cat > ChangeLog <<EOT
This file is a placeholder. It will be replaced with the actual ChangeLog
by make dist. Run make ChangeLog if you wish to create it earlier.
EOT
-autoreconf -f -i -s \ No newline at end of file
+autoreconf -f -i -s
diff --git a/configure.ac b/configure.ac
index 8d1625a..4f460cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,7 @@ AC_PROG_RANLIB
# Checks for header files.
AC_HEADER_STDC
+AC_CHECK_HEADERS([getopt.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
@@ -41,7 +42,7 @@ AC_TYPE_UINT32_T
AC_TYPE_SIGNAL
# Checks for library functions.
-AC_CHECK_FUNCS([sysconf getdtablesize])
+AC_CHECK_FUNCS([sysconf getdtablesize getopt_long])
AC_REPLACE_FUNCS([daemon])
# Check for TCP wrappers
@@ -79,5 +80,9 @@ if test "$status_tcpwrap" = "yes"; then
AC_DEFINE([USE_TCPWRAP],1,[Define if libwrap is used])
fi
-AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile])
+GRECS_SETUP([grecs],[std-pp-setup])
+
+AC_CONFIG_FILES([Makefile
+ src/Makefile
+ doc/Makefile])
AC_OUTPUT
diff --git a/doc/johd.8 b/doc/johd.8
index c8a6f87..bae96ca 100644
--- a/doc/johd.8
+++ b/doc/johd.8
@@ -131,7 +131,7 @@ command line is encountered, whichever occurs first.
.TP
\fB\-S\fR \fINAME\fR
Sets service name for TCP wrappers. This option affects all
-subsequent \fB-l\fI options appearing to the right of it, until
+subsequent \fB-l\fR options appearing to the right of it, until
another \fB-S\fR option or end of command line is encountered,
whichever occurs first.
diff --git a/git2chg.awk b/git2chg.awk
deleted file mode 100644
index 0692a54..0000000
--- a/git2chg.awk
+++ /dev/null
@@ -1,45 +0,0 @@
-# This file is part of Smap.
-# Copyright (C) 2010 Sergey Poznyakoff
-#
-# Smap 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.
-#
-# Smap 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 Smap. If not, see <http://www.gnu.org/licenses/>.
-
-/^[0-9]+ .* +<[^>]+>/ {
- s = strftime("%F", $1)
- sub(/^[0-9]+ +/,"")
- if (s == datestr && author == $0)
- next
- datestr = s
- author = $0
- if (runlen) { runlen = 0; print "" }
- printf("%s %s\n", datestr, author)
- next
-}
-/^Signed-off-by:/ { next }
-/^<unknown>$/ { next }
-NF==0 {
- runlen++
- next
-}
-{ if (runlen) { runlen = 0; print "" }
- print "\t" $0 }
-
-END {
- print "\f"
- # Make sure Emacs won't recognize this line:
- print "Local", "Variables:"
- print "mode: change-log"
- print "version-control: never"
- print "buffer-read-only: t"
- print "End:"
-}
diff --git a/grecs b/grecs
new file mode 160000
+Subproject 2614146e13e93c69a26907ef25fca89f5b84545
diff --git a/src/.gitignore b/src/.gitignore
index a1e1ae6..62e1834 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
johd
joh.cgi
404.c
+cmdline.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 7f09dfc..eba27cc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,8 +18,8 @@ sbin_PROGRAMS = johd
libexec_PROGRAMS = joh.cgi
noinst_LIBRARIES = libjoh.a
-EXTRA_DIST = daemon.c html2cstr.sed 404.html 404.c
-BUILT_SOURCES = 404.c
+EXTRA_DIST = daemon.c html2cstr.sed 404.html 404.c cmdline.opt
+BUILT_SOURCES = 404.c cmdline.h
.html.c:
$(AM_V_GEN)sed 's/%PACKAGE%/$(PACKAGE_NAME)/g;s/%VERSION%/$(VERSION)/g' $< | \
@@ -41,11 +41,14 @@ libjoh_a_SOURCES = \
johd_SOURCES = \
$(ACL_C)\
base64.c\
+ cmdline.h\
+ config.c\
conntab.c\
get.c\
interp.c\
joh.h\
johd.c\
+ hintab.c\
http.c\
key.c\
pdu.c\
@@ -61,5 +64,15 @@ johd_SOURCES = \
joh_cgi_SOURCES = \
johcgi.c
-LDADD = ./libjoh.a
+INCLUDES = @GRECS_INCLUDES@
+LDADD = ./libjoh.a @GRECS_LDADD@
+AM_CPPFLAGS= \
+ -DSYSCONFDIR=\"$(sysconfdir)\"\
+ -DDEFAULT_VERSION_INCLUDE_DIR=\"$(incdir)\"\
+ -DDEFAULT_INCLUDE_DIR=\"$(pkgdatadir)/include\"\
+ -DDEFAULT_PREPROCESSOR="$(DEFAULT_PREPROCESSOR)"
+SUFFIXES = .opt .h
+
+.opt.h:
+ $(AM_V_GEN)m4 -s $(top_srcdir)/grecs/build-aux/getopt.m4 $< > $@
diff --git a/src/cmdline.opt b/src/cmdline.opt
new file mode 100644
index 0000000..5098ff8
--- /dev/null
+++ b/src/cmdline.opt
@@ -0,0 +1,262 @@
+/* johd: Jabber-Over-HTTP daemon. -*- c -*-
+ Copyright (C) 2011 Sergey Poznyakoff
+
+ JOH 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.
+
+ JOH 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 JOH. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+
+static char *pp_cmd_buffer;
+static size_t pp_cmd_bufsize;
+static size_t pp_cmd_buflevel;
+
+static unsigned next_hint_idx;
+static char *next_hint_name_ptr;
+static size_t next_hint_name_size;
+
+static void
+next_hint()
+{
+ struct joh_sockaddr_hints *hint;
+
+ grecs_asprintf(&next_hint_name_ptr, &next_hint_name_size,
+ "%u", next_hint_idx++);
+ hint = joh_get_hint_by_name(next_hint_name_ptr, 1);
+ memcpy((char*)hint + offsetof(struct joh_sockaddr_hints, flags),
+ (char*)&listen_hints +
+ offsetof(struct joh_sockaddr_hints, flags),
+ sizeof(struct joh_sockaddr_hints) -
+ offsetof(struct joh_sockaddr_hints, flags));
+}
+
+OPTIONS_BEGIN("johd",
+ [<johd provides a jabber-over-HTTP tunnel>],[<>],
+ [<gnu>],
+ [<copyright_year=2011>],
+ [<copyright_holder=Sergey Poznyakoff>])
+
+OPTION(lint,t,,
+ [<parse configuration file and exit>])
+BEGIN
+ lint_mode = 1;
+ log_to_stderr = 1;
+END
+
+OPTION(config-file,C,FILE,
+ [<use FILE instead of the default configuration>])
+BEGIN
+ conffile = optarg;
+END
+
+OPTION(class,c,CLASS,
+ [<set client class (CGI or HTTP) for subsequent -l options>])
+BEGIN
+ if (strcasecmp(optarg, "HTTP") == 0) {
+ listen_hints.class = client_http;
+ listen_hints.service = "80";
+ } else if (strcasecmp(optarg, "CGI") == 0) {
+ listen_hints.class = client_cgi;
+ listen_hints.service = DEFAULT_PORT_STRING;
+ } else
+ die(EX_USAGE,
+ "unrecognized client class: %s", optarg);
+ next_hint();
+END
+
+OPTION(listen,l,URL,
+ [<listen on the given URL (several -l options are OK)>])
+BEGIN
+ if (next_hint_name_ptr) {
+ char *str = NULL;
+ size_t size = 0;
+
+ grecs_asprintf(&str, &size, "%s %s", optarg,
+ next_hint_name_ptr);
+ OPTNODE(".listen", str);
+ free(str);
+ } else
+ OPTNODE(".listen", optarg);
+END
+
+OPTION(server,s,URL,
+ [<jabber server>])
+BEGIN
+ OPTNODE(".server", optarg);
+END
+
+OPTION(tcpd-service,S,NAME,
+ [<set service name for TCP wrappers>])
+BEGIN
+ listen_hints.flags |= JOH_HINT_SRVNAME;
+ listen_hints.srvname = optarg;
+ next_hint();
+END
+
+OPTION(timeout,T,[<CLASS:TIMEOUT>],
+ [<set session idle timeout>])
+BEGIN
+ OPTNODE(".timeout", optarg);
+END
+
+dnl FIXME: short letter
+OPTION(daemon,a,,
+ [<after startup, switch to the background>])
+BEGIN
+ OPTNODE(".standalone", "yes");
+END
+
+OPTION(pidfile,p,FILE,
+ [<write PID to FILE>])
+BEGIN
+ OPTNODE(".pidfile", optarg);
+END
+
+OPTION(user,u,NAME,
+ [<after startup, switch to privileges of the user NAME>])
+BEGIN
+ OPTNODE(".user", optarg);
+END
+
+GROUP([<HTTP responses>])
+
+OPTION(error-page,E,FILE,
+ [<read 404 error page from FILE>])
+BEGIN
+ new_get_reply(&listen_hints, 0, optarg);
+END
+
+OPTION(redirect,R,URL,
+ [<redirect GET requests to URL>])
+BEGIN
+ new_get_reply(&listen_hints, 1, optarg);
+END
+
+GROUP([<Logging and debugging options>])
+
+OPTION(debug,d,CAT[.LEVEL],
+ [<set debugging level>])
+BEGIN
+ if (strncmp(optarg, "cfgram", 6) == 0 ||
+ strncmp(optarg, "cflex", 5) == 0) {
+ if (parse_debug_level(optarg))
+ die(EX_USAGE,
+ "invalid debugging category or level: %s",
+ optarg);
+ } else
+ OPTNODE(".debug", optarg);
+END
+
+OPTION(facility,F,FACILITY,
+ [<set syslog facility>])
+BEGIN
+ OPTNODE(".syslog.facility", optarg);
+END
+
+OPTION(line-info,i,,
+ [<show source line information with debug messages>])
+BEGIN
+ OPTNODE(".line-info", optarg);
+END
+
+OPTION(log-tag,L,STRING,
+ [<set log tag>])
+BEGIN
+ OPTNODE(".syslog.tag", optarg);
+END
+
+OPTION(print-priority,P,,
+ [<prefix diagnostic messages with their severity level>])
+BEGIN
+ OPTNODE(".syslog.print-priority", optarg);
+END
+
+GROUP(Preprocessor control)
+
+OPTION(include-directory,I,DIR,
+ [<add include directory>])
+BEGIN
+ grecs_preproc_add_include_dir(optarg);
+END
+
+OPTION(define,D,SYMBOL[=VALUE],
+ [<define a preprocessor symbol>])
+BEGIN
+ size_t len;
+ char *p;
+
+ len = 4;
+ for (p = optarg; *p; p++) {
+ if (*p == '\\' || *p == '"')
+ len++;
+ len++;
+ }
+
+ if (pp_cmd_buflevel + len + 1 > pp_cmd_bufsize) {
+ pp_cmd_bufsize = pp_cmd_buflevel + len + 1;
+ pp_cmd_buffer = grecs_realloc(pp_cmd_buffer, pp_cmd_bufsize);
+ }
+ memcpy(pp_cmd_buffer + pp_cmd_buflevel, " \"-D", 4);
+ pp_cmd_buflevel += 4;
+
+ p = optarg;
+ do {
+ if (*p == '\\' || *p == '"')
+ pp_cmd_buffer[pp_cmd_buflevel++] = '\\';
+ pp_cmd_buffer[pp_cmd_buflevel++] = *p;
+ } while (*p++);
+END
+
+OPTION(preprocessor,,COMMAND,
+ [<use COMMAND instead of the default preprocessor>])
+BEGIN
+ grecs_preprocessor = optarg;
+END
+
+OPTION(no-preprocessor,,,
+ [<disable preprocessing>])
+BEGIN
+ grecs_preprocessor = NULL;
+END
+
+GROUP([<Additional help>])
+OPTION(config-help,,,
+ [<show configuration file summary>])
+BEGIN
+ config_help();
+ exit(0);
+END
+
+OPTIONS_END
+
+void
+joh_version_hook(FILE *fp)
+{
+ struct grecs_version_info *vinfo;
+ vinfo = grecs_version();
+ fprintf(fp, "With %s version %s\n",
+ strcmp(vinfo->package, PACKAGE_NAME) == 0 ?
+ "built-in grecs library" : vinfo->package,
+ vinfo->version);
+ fprintf(fp, "ID: %s\n", vinfo->id);
+ grecs_version_info_free(vinfo);
+}
+
+void
+parse_options(int argc, char *argv[])
+{
+ print_version_hook = joh_version_hook;
+ GETOPT(argc, argv, , exit(EX_USAGE))
+}
+
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..491db56
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,461 @@
+/* Diagnostic functions for JOH.
+ Copyright (C) 2011 Sergey Poznyakoff
+
+ JOH 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.
+
+ JOH 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 JOH. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "joh.h"
+#include <ctype.h>
+#include <string.h>
+#include <syslog.h>
+
+struct grecs_list *cfg_debug_spec;
+
+static int
+cb_syslog_facility(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, _("Unexpected block statement"));
+ return 1;
+ }
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, _("expected string argument"));
+ return 1;
+ }
+
+ if (string_to_syslog_facility(value->v.string, varptr))
+ grecs_error(locus, 0, _("Unknown syslog facility `%s'"),
+ value->v.string);
+ return 0;
+}
+
+static struct grecs_keyword syslog_kw[] = {
+ { "facility", N_("name"),
+ N_("Set syslog facility. Arg is one of the following: user, daemon, "
+ "auth, authpriv, mail, cron, local0 through local7 (case-insensitive), "
+ "or a facility number."),
+ grecs_type_string|GRECS_AGGR, &syslog_facility, 0,
+ cb_syslog_facility },
+ { "tag", N_("string"), N_("Tag syslog messages with this string"),
+ grecs_type_string, &syslog_tag },
+ { "print-priority", N_("arg"),
+ N_("Prefix each message with its priority"),
+ grecs_type_bool, &syslog_include_prio },
+ { NULL },
+};
+
+static int
+cb_class_type(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ struct joh_sockaddr_hints *hints = varptr;
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, _("Unexpected block statement"));
+ return 1;
+ }
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, _("expected string argument"));
+ return 1;
+ }
+
+ if (strcasecmp(value->v.string, "HTTP") == 0) {
+ hints->class = client_http;
+ hints->service = "80";
+ } else if (strcasecmp(value->v.string, "CGI") == 0) {
+ hints->class = client_cgi;
+ hints->service = DEFAULT_PORT_STRING;
+ } else {
+ grecs_error(locus, 0,
+ "unrecognized client class: %s",
+ value->v.string);
+ }
+ hints->flags |= JOH_HINT_SERVICE;
+ return 0;
+}
+
+static int
+cb_http_common(int redirect,
+ enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ struct joh_sockaddr_hints *hints = varptr;
+
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, _("Unexpected block statement"));
+ return 1;
+ }
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, _("expected string argument"));
+ return 1;
+ }
+ new_get_reply(hints, redirect, value->v.string);
+ return 0;
+}
+
+static int
+cb_class_http_error_page(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ return cb_http_common(0, cmd, locus, varptr, value, cb_data);
+}
+
+static int
+cb_class_http_redirect(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ return cb_http_common(1, cmd, locus, varptr, value, cb_data);
+}
+
+/*
+ class NAME {
+ type CGI|HTTP;
+ service NAME;
+ http-error-page FILE;
+ http-redirect URL;
+ }
+*/
+static struct grecs_keyword class_kw[] = {
+ { "type", "<arg: CGI | HTTP>", "Set class type (default: HTTP)",
+ grecs_type_string|GRECS_AGGR, NULL, 0, cb_class_type },
+ { "service", NULL, "Service name for TCP wrappers",
+ grecs_type_string|GRECS_AGGR, NULL,
+ offsetof(struct joh_sockaddr_hints, srvname) },
+ { "http-error-page", "file", "Read HTTP error page from <file>",
+ grecs_type_string|GRECS_AGGR, NULL, 0,
+ cb_class_http_error_page },
+ { "http-redirect", "URL", "Redirect HTTP GET requests to URL",
+ grecs_type_string|GRECS_AGGR, NULL, 0,
+ cb_class_http_redirect },
+ { NULL }
+};
+
+static int
+cb_class(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ struct joh_sockaddr_hints *hints;
+ void **pdata = cb_data;
+
+ switch (cmd) {
+ case grecs_callback_section_begin:
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, _("tag must be a string"));
+ return 0;
+ }
+ hints = joh_get_hint_by_name(value->v.string, 1);
+ *pdata = hints;
+ break;
+ case grecs_callback_section_end:
+ hints = *pdata;
+ if (hints->class == client_unused)
+ hints->class = client_http;
+ hints->flags |= JOH_HINT_CLASS;
+ if (hints->srvname)
+ hints->flags |= JOH_HINT_SRVNAME;
+
+ break;
+ case grecs_callback_set_value:
+ grecs_error(locus, 0, _("invalid use of block statement"));
+ }
+ return 0;
+}
+
+
+static int
+cb_listen(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ char *url;
+ struct joh_sockaddr_hints *hints = &listen_hints;
+
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, _("Unexpected block statement"));
+ return 1;
+ }
+ if (!value || value->type == GRECS_TYPE_LIST) {
+ grecs_error(locus, 0, _("expected string argument"));
+ return 1;
+ }
+ if (value->type == GRECS_TYPE_STRING)
+ url = value->v.string;
+ else if (value->v.arg.c > 2) {
+ grecs_error(locus, 0, _("too many argument"));
+ return 1;
+ } else {
+ if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, _("URL must be a string"));
+ return 1;
+ }
+ url = value->v.arg.v[0]->v.string;
+
+ if (value->v.arg.c == 2) {
+ if (value->v.arg.v[1]->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0,
+ _("class name must be a string"));
+ return 1;
+ }
+ hints =
+ joh_get_hint_by_name(value->v.arg.v[1]->v.string, 0);
+ if (!hints) {
+ grecs_error(locus, 0,
+ _("class %s not defined"),
+ value->v.arg.v[1]->v.string);
+ return 1;
+ }
+ }
+ }
+
+ listen_addrs = joh_sockaddr_append(listen_addrs,
+ parse_sockaddr(url, hints));
+ return 0;
+}
+
+static int
+cb_server(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, _("Unexpected block statement"));
+ return 1;
+ }
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, _("expected string argument"));
+ return 1;
+ }
+ jabber_addr = parse_sockaddr(value->v.string, &jabber_hints);
+ if (jabber_addr->next) {
+ warn("%s resolves to more than one address, using %s",
+ optarg, joh_sockaddr_str(jabber_addr));
+ joh_sockaddr_free(jabber_addr->next);
+ jabber_addr->next = NULL;
+ }
+ return 0;
+}
+
+static void
+set_timeout(enum joh_conntype sesstype, char *arg, grecs_locus_t *loc)
+{
+ time_t timeout;
+ int ind = -1;
+ static struct { char s[3]; unsigned f; } tab[] = {
+ { "hH", 3600 },
+ { "mM", 60 },
+ { "sS", 1 }
+ };
+
+ if (sesstype == conn_unused) {
+ switch (*arg) {
+ case 'j':
+ case 'J':
+ sesstype = conn_jabber;
+ break;
+ case 'c':
+ case 'C':
+ sesstype = conn_client;
+ break;
+ default:
+ grecs_error(loc, 0,
+ "bad type in timeout specification: %s",
+ arg);
+ return;
+ }
+ if (*++arg != ':') {
+ grecs_error(loc, 0,
+ "timeout specification missing colon: %s",
+ arg);
+ return;
+ }
+ }
+
+ timeout = 0;
+ for (;;) {
+ unsigned long t = strtoul(arg + 1, &arg, 10);
+ if (*arg) {
+ unsigned x = 0;
+
+ for (ind++; ind<sizeof(tab)/sizeof(tab[0]);
+ ind++) {
+ if (strchr(tab[ind].s, *arg)) {
+ x = tab[ind].f;
+ break;
+ }
+ }
+ if (x == 0) {
+ grecs_error(loc, 0, "unknown time suffix %c",
+ *arg);
+ return;
+ }
+ timeout += t * x;
+ if (x > 1)
+ continue;
+ }
+ break;
+ }
+ if (*arg) {
+ grecs_error(loc, 0, "invalid timeout specification: %s", arg);
+ return;
+ }
+ joh_session_set_timeout(sesstype, timeout);
+}
+
+static int
+cb_timeout(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ enum joh_conntype session = conn_unused;
+ char *timestr;
+
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, _("Unexpected block statement"));
+ return 1;
+ }
+ if (!value) {
+ grecs_error(locus, 0, _("required arguments missing"));
+ return 1;
+ }
+ if (value->type == GRECS_TYPE_STRING)
+ timestr = value->v.string;
+ else if (value->type == GRECS_TYPE_ARRAY && value->v.arg.c == 2) {
+ char *s;
+
+ if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0,
+ _("session type not a string"));
+ return 1;
+ }
+ if (value->v.arg.v[1]->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0,
+ _("timeout not a string"));
+ return 1;
+ }
+ s = value->v.arg.v[0]->v.string;
+ if (strcmp(s, "j") || strcmp(s, "jabber")) {
+ session = conn_jabber;
+ } else if (strcmp(s, "c") || strcmp(s, "client")) {
+ session = conn_client;
+ } else {
+ grecs_error(locus, 0, _("unrecognized session type"));
+ return 1;
+ }
+ timestr = value->v.arg.v[1]->v.string;
+ } else {
+ grecs_error(locus, 0, _("invalid arguments"));
+ return 1;
+ }
+
+ set_timeout(session, timestr, locus);
+ return 0;
+}
+
+static struct grecs_keyword joh_kw[] = {
+ { "debug", NULL, "Set debug level",
+ grecs_type_string|GRECS_LIST, &cfg_debug_spec },
+ { "syslog", NULL, "Configure syslog logging",
+ grecs_type_section, NULL, 0, NULL, NULL, syslog_kw },
+ { "line-info", NULL,
+ "Include source line information in debugging messages",
+ grecs_type_bool, &syslog_source_info },
+ { "pidfile", "file", "Write PID to <file>",
+ grecs_type_string, &pidfile },
+ { "standalone", NULL, "Standalone mode",
+ grecs_type_bool, &daemon_option },
+ { "user", NULL, "Run as this user",
+ grecs_type_string, &user_name },
+ { "class", "name: string", "Define socket class",
+ grecs_type_section, NULL, 0, cb_class, NULL, class_kw },
+ { "timeout",
+ "session: c(lient) or j(abber)> <arg: timespec",
+ "Set session timeout",
+ grecs_type_string|GRECS_AGGR, NULL, 0, cb_timeout },
+ { "listen", "url: string> <class: name", "Listen on a socket",
+ grecs_type_string|GRECS_MULT, NULL, 0, cb_listen },
+ { "server", "url", "Set Jabber server URL",
+ grecs_type_string, NULL, 0, cb_server },
+ { NULL }
+};
+
+void
+config_help()
+{
+ static char docstring[] =
+ N_("Configuration file structure for johd.\n"
+ "For more information, see johd(8).");
+ grecs_format_docstring(docstring, 0, stdout);
+ grecs_format_statement_array(joh_kw, 1, 0, stdout);
+}
+
+void
+config_init()
+{
+ grecs_include_path_setup(DEFAULT_VERSION_INCLUDE_DIR,
+ DEFAULT_INCLUDE_DIR, NULL);
+ grecs_preprocessor = DEFAULT_PREPROCESSOR;
+ grecs_log_to_stderr = 1;
+}
+
+void
+flush_debug_levels(struct grecs_list *lp)
+{
+ struct grecs_list_entry *ep;
+
+ if (!lp)
+ return;
+ for (ep = lp->head; ep; ep = ep->next) {
+ if (parse_debug_level(ep->data))
+ die(EX_CONFIG,
+ "invalid debugging category or level: %s",
+ (char*)ep->data);
+ }
+ grecs_list_free(lp);
+}
+
+void
+config_finish(struct grecs_node *tree)
+{
+ grecs_tree_reduce(tree, joh_kw, GRECS_AGGR);
+ if (grecs_tree_process(tree, joh_kw))
+ exit(EX_CONFIG);
+ flush_debug_levels(cfg_debug_spec);
+ grecs_tree_free(tree);
+}
+
+
+
diff --git a/src/error.c b/src/error.c
index 8d7a3cc..7cf0df3 100644
--- a/src/error.c
+++ b/src/error.c
@@ -20,11 +20,14 @@
#include <syslog.h>
const char *program_name;
-int debug_level;
+int debug_level[_JOH_DEBCAT_MAX];
+int log_to_stderr = -1;
+const char *syslog_tag = NULL;
+int syslog_include_prio = 0;
+int syslog_facility = LOG_DAEMON;
+int syslog_source_info = 0;
+
int source_info_option;
-int prefix_severity_option;
-char *facility_option;
-char *log_tag;
void
set_program_name(const char *arg)
@@ -37,6 +40,70 @@ set_program_name(const char *arg)
}
+struct debug_trans {
+ const char *name;
+ size_t length;
+ int cat;
+};
+
+static struct debug_trans debug_trans[] = {
+#define S(s) #s, sizeof(#s)-1
+ { S(main), JOH_DEBCAT_MAIN },
+ { S(http), JOH_DEBCAT_HTTP },
+ { S(proto), JOH_DEBCAT_PROTO },
+ { S(sess), JOH_DEBCAT_SESS },
+ { S(cfgram), JOH_DEBCAT_CFGRAM },
+ { S(cflex), JOH_DEBCAT_CFLEX },
+ { NULL }
+};
+
+int
+parse_debug_level(const char *arg)
+{
+ unsigned long cat, lev;
+ char *p = NULL;
+
+ if (isascii(*arg) && isdigit(*arg)) {
+ lev = strtoul(arg, &p, 10);
+ if (*p == 0) {
+ for (cat = 0; cat < _JOH_DEBCAT_MAX; cat++)
+ debug_level[cat] = lev;
+ return 0;
+ } else if (*p != '.')
+ return -1;
+ cat = lev;
+ if (cat > _JOH_DEBCAT_MAX)
+ return -1;
+ lev = 0;
+ } else {
+ size_t len = strcspn(arg, ".");
+ struct debug_trans *dp;
+
+ for (dp = debug_trans; dp->name; dp++)
+ if (dp->length == len &&
+ memcmp(dp->name, arg, len) == 0)
+ break;
+
+ if (!dp->name)
+ return -1;
+ cat = dp->cat;
+ p = (char*) arg + len;
+ }
+
+ if (*p == 0)
+ lev = 100;
+ else if (*p != '.')
+ return -1;
+ else {
+ lev = strtoul(p + 1, &p, 10);
+ if (*p)
+ return -1;
+ }
+ debug_level[cat] = lev;
+ return 0;
+}
+
+
#define JOH_SEVERITY_DEBUG 0
#define JOH_SEVERITY_WARN 1
#define JOH_SEVERITY_INFO 2
@@ -56,7 +123,7 @@ vlogmsg_stderr(int severity, const char *file, unsigned line,
const char *fmt, va_list ap)
{
fprintf(stderr, "%s: ", program_name);
- if (prefix_s