diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-08 00:23:38 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-09 13:29:28 +0300 |
commit | f85feb9fca07842eead909b83dd55d6a07392b7d (patch) | |
tree | 70d2a123aeba7e5ef59a47aec4187e9d31969b5d | |
parent | 5dd4d8742941e15967dec29e84756485986b7e36 (diff) | |
download | joh-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-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rwxr-xr-x | bootstrap | 5 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | doc/johd.8 | 2 | ||||
-rw-r--r-- | git2chg.awk | 45 | ||||
m--------- | grecs | 0 | ||||
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 19 | ||||
-rw-r--r-- | src/cmdline.opt | 262 | ||||
-rw-r--r-- | src/config.c | 461 | ||||
-rw-r--r-- | src/error.c | 123 | ||||
-rw-r--r-- | src/hintab.c | 90 | ||||
-rw-r--r-- | src/http.c | 25 | ||||
-rw-r--r-- | src/joh.h | 50 | ||||
-rw-r--r-- | src/johcgi.c | 3 | ||||
-rw-r--r-- | src/johd.c | 326 | ||||
-rw-r--r-- | src/key.c | 3 | ||||
-rw-r--r-- | src/pdubuf.c | 3 | ||||
-rw-r--r-- | src/selloop.c | 19 | ||||
-rw-r--r-- | src/sess.c | 16 | ||||
-rw-r--r-- | src/socket.c | 16 | ||||
-rw-r--r-- | src/transparent.c | 9 |
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 @@ -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 @@ -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 |