diff --git a/Makefile b/Makefile index 8b1ced5..b1e2a46 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ BINARY = $(NAME) VERSION = 3.1 CODENAME = Dungeon Edition -SOURCES = src/$(NAME).c src/file.c src/menu.c src/string.c src/platform.c src/session.c src/options.c +SOURCES = src/$(NAME).c src/file.c src/menu.c src/string.c src/platform.c src/session.c src/options.c src/log.c HEADERS = src/files.h src/filetypes.h OBJECTS = $(SOURCES:.c=.o) README = README.md diff --git a/gophernicus.1 b/gophernicus.1 index 3494e89..29dd864 100644 --- a/gophernicus.1 +++ b/gophernicus.1 @@ -202,6 +202,11 @@ Print debug output in .Xr syslog 3 and .Pa /server-status . +When +.Fl ns +(disable +.Xr syslog 3 ) +is used this option has no effect. .It Fl v Display version information and build date. .It Fl b diff --git a/src/file.c b/src/file.c index 2e1e31a..391a80e 100644 --- a/src/file.c +++ b/src/file.c @@ -40,7 +40,7 @@ void send_binary_file(state *st) int fd; off_t offset = 0; - if (st->debug) syslog(LOG_INFO, "outputting binary file \"%s\"", st->req_realpath); + log_debug("send binary file \"%s\"", st->req_realpath); if ((fd = open(st->req_realpath, O_RDONLY)) == ERROR) return; sendfile(1, fd, &offset, st->req_filesize); @@ -52,7 +52,7 @@ void send_binary_file(state *st) char buf[BUFSIZE]; int bytes; - if (st->debug) syslog(LOG_INFO, "outputting binary file \"%s\"", st->req_realpath); + log_debug("send binary file \"%s\"", st->req_realpath); if ((fp = fopen(st->req_realpath , "r")) == NULL) return; while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0) @@ -72,7 +72,8 @@ void send_text_file(state *st) char out[BUFSIZE]; int line; - if (st->debug) syslog(LOG_INFO, "outputting text file \"%s\"", st->req_realpath); + log_debug("sending text file \"%s\"", st->req_realpath); + if ((fp = fopen(st->req_realpath , "r")) == NULL) return; /* Loop through the file line by line */ @@ -118,15 +119,13 @@ void url_redirect(state *st) sstrncmp(dest, "mailto:") != MATCH) die(st, ERR_ACCESS, "Refusing to HTTP redirect unsafe protocols"); - /* Log the redirect */ - if (st->opt_syslog) { - syslog(LOG_INFO, "request for \"gopher%s://%s:%i/h%s\" from %s", - (st->server_port == st->server_tls_port ? "s" : ""), - st->server_host, - st->server_port, - st->req_selector, - st->req_remote_addr); - } + log_info("request for \"gopher%s://%s:%i/h%s\" from %s", + st->server_port == st->server_tls_port ? "s" : "", + st->server_host, + st->server_port, + st->req_selector, + st->req_remote_addr); + log_combined(st, HTTP_OK); /* Output HTML */ @@ -155,14 +154,12 @@ void server_status(state *st, shm_state *shm, int shmid) int sessions; int i; - /* Log the request */ - if (st->opt_syslog) { - syslog(LOG_INFO, "request for \"gopher%s://%s:%i/0" SERVER_STATUS "\" from %s", - (st->server_port == st->server_tls_port ? "s" : ""), - st->server_host, - st->server_port, - st->req_remote_addr); - } + log_info("request for \"gopher%s://%s:%i/0" SERVER_STATUS "\" from %s", + st->server_port == st->server_tls_port ? "s" : "", + st->server_host, + st->server_port, + st->req_remote_addr); + log_combined(st, HTTP_OK); /* Quit if shared memory isn't initialized yet */ @@ -230,14 +227,12 @@ void server_status(state *st, shm_state *shm, int shmid) */ void caps_txt(state *st, shm_state *shm) { - /* Log the request */ - if (st->opt_syslog) { - syslog(LOG_INFO, "request for \"gopher%s://%s:%i/0" CAPS_TXT "\" from %s", - (st->server_port == st->server_tls_port ? "s" : ""), - st->server_host, - st->server_port, - st->req_remote_addr); - } + log_info("request for \"gopher%s://%s:%i/0" CAPS_TXT "\" from %s", + st->server_port == st->server_tls_port ? "s" : "", + st->server_host, + st->server_port, + st->req_remote_addr); + log_combined(st, HTTP_OK); /* Update counters */ @@ -369,13 +364,14 @@ static void run_cgi(state *st, char *script, char *arg) { if (st->opt_exec) { - /* Setup environment & execute the binary */ - if (st->debug) syslog(LOG_INFO, "executing script \"%s\"", script); + /* Setup environment & execute the binary */ + log_debug("executing script \"%s\"", script); - setenv_cgi(st, script); - execl(script, script, arg, NULL); + setenv_cgi(st, script); + execl(script, script, arg, NULL); + } else { + log_debug("execution of script \"%s\" blocked by `-nx'", script); } - else if (st->debug) syslog(LOG_INFO, "script \"%s\" was blocked by -nx", script); /* Didn't work - die */ die(st, ERR_ACCESS, NULL); diff --git a/src/gophernicus.c b/src/gophernicus.c index d0a5c44..412ea36 100644 --- a/src/gophernicus.c +++ b/src/gophernicus.c @@ -34,11 +34,12 @@ * Libwrap needs these defined */ #ifdef HAVE_LIBWRAP +#include + int allow_severity = LOG_DEBUG; int deny_severity = LOG_ERR; #endif - /* * Print gopher menu line */ @@ -112,17 +113,12 @@ void footer(state *st) */ void die(state *st, const char *message, const char *description) { - int en = errno; static const char error_gif[] = ERROR_GIF; - /* Handle NULL description */ - if (description == NULL) description = strerror(en); + log_fatal("error \"%s %s\" for request \"%s\" from %s", + message, description ? description : "", + st->req_selector, st->req_remote_addr); - /* Log the error */ - if (st->opt_syslog) { - syslog(LOG_ERR, "error \"%s %s\" for request \"%s\" from %s", - message, description, st->req_selector, st->req_remote_addr); - } log_combined(st, HTTP_404); /* Handle menu errors */ @@ -221,10 +217,8 @@ static void selector_to_path(state *st) st->rewrite[i].replace, st->req_selector + strlen(st->rewrite[i].match)); - if (st->debug) { - syslog(LOG_INFO, "rewriting selector \"%s\" -> \"%s\"", - st->req_selector, buf); - } + log_debug("rewriting selector \"%s\" -> \"%s\"", + st->req_selector, buf); sstrlcpy(st->req_selector, buf); } @@ -530,8 +524,7 @@ int main(int argc, char *argv[]) /* Handle command line arguments */ parse_args(&st, argc, argv); - /* Open syslog() */ - if (st.opt_syslog) openlog(self, LOG_PID, LOG_DAEMON); + log_init(st.opt_syslog, st.debug); #ifdef __OpenBSD__ /* unveil(2) support. @@ -543,8 +536,7 @@ int main(int argc, char *argv[]) if (st.extra_unveil_paths != NULL) { die(&st, NULL, "-U and executable maps cannot co-exist"); } - if (st.debug) - syslog(LOG_INFO, "executable gophermaps are enabled, no unveil(2)"); + log_debug("executable gophermaps are enabled, no unveil(2)"); } else { if (unveil(st.server_root, "r") == -1) die(&st, NULL, "unveil"); @@ -556,8 +548,7 @@ int main(int argc, char *argv[]) * to unveil it anyway. */ if (st.opt_personal_spaces) { - if (st.debug) - syslog(LOG_INFO, "unveiling /etc/pwd.db"); + log_debug("unveiling /etc/pwd.db"); if (unveil("/etc/pwd.db", "r") == -1) die(&st, NULL, "unveil"); } @@ -569,8 +560,7 @@ int main(int argc, char *argv[]) if (*extra_unveil == '\0') continue; /* empty path */ - if (st.debug) - syslog(LOG_INFO, "unveiling extra path: %s\n", extra_unveil); + log_debug("unveiling extra path: %s\n", extra_unveil); if (unveil(extra_unveil, "r") == -1) die(&st, NULL, "unveil"); } @@ -582,8 +572,7 @@ int main(int argc, char *argv[]) /* pledge(2) support */ if (st.opt_shm) { /* pledge(2) never allows shared memory */ - if (st.debug) - syslog(LOG_INFO, "shared-memory enabled, can't pledge(2)"); + log_debug("shared-memory enabled, can't pledge(2)"); } else { strlcpy(pledges, "stdio rpath inet sendfd recvfd proc", @@ -592,21 +581,13 @@ int main(int argc, char *argv[]) /* Executable maps shell-out using popen(3) */ if (st.opt_exec) { strlcat(pledges, " exec", sizeof(pledges)); - if (st.debug) { - syslog(LOG_INFO, - "executable gophermaps enabled, " - "adding 'exec' to pledge(2)"); - } + log_debug("executable gophermaps enabled, adding `exec' to pledge(2)"); } /* Personal spaces require getpwnam(3) and getpwent(3) */ if (st.opt_personal_spaces) { strlcat(pledges, " getpw", sizeof(pledges)); - if (st.debug) { - syslog(LOG_INFO, - "personal gopherspaces enabled, " - "adding 'getpw' to pledge(2)"); - } + log_debug("personal gopherspaces enabled, adding `getpw' to pledge(2)"); } if (pledge(pledges, NULL) == -1) @@ -680,12 +661,12 @@ get_selector: /* Remove trailing CRLF */ chomp(selector); - if (st.debug) syslog(LOG_INFO, "client sent us \"%s\"", selector); + log_debug("client sent \"%s\"", selector); /* Handle HAproxy/Stunnel proxy protocol v1 */ #ifdef ENABLE_HAPROXY1 if (sstrncmp(selector, "PROXY TCP") == MATCH && st.opt_proxy) { - if (st.debug) syslog(LOG_INFO, "got proxy protocol header \"%s\"", selector); + log_debug("got proxy protocol header \"%s\"", selector); sscanf(selector, "PROXY TCP%d %s %s %d %d", &dummy, remote, local, &dummy, &st.server_port); @@ -716,7 +697,7 @@ get_selector: printf("+VIEWS:" CRLF " application/gopher+-menu: <512b>" CRLF); printf("." CRLF); - if (st.debug) syslog(LOG_INFO, "got a request for gopher+ root menu"); + log_debug("got a request for gopher+ root menu"); return OK; } @@ -730,7 +711,7 @@ get_selector: st.req_protocol = PROTO_HTTP; - if (st.debug) syslog(LOG_INFO, "got HTTP request for \"%s\"", selector); + log_debug("got HTTP request for \"%s\"", selector); } /* Save default server_host & fetch session data (including new server_host) */ @@ -800,7 +781,7 @@ get_selector: /* Convert seletor to path & stat() */ selector_to_path(&st); - if (st.debug) syslog(LOG_INFO, "path to resource is \"%s\"", st.req_realpath); + log_debug("path to resource is \"%s\"", st.req_realpath); if (stat(st.req_realpath, &file) == ERROR) { @@ -858,15 +839,13 @@ get_selector: #endif /* Log the request */ - if (st.opt_syslog) { - syslog(LOG_INFO, "request for \"gopher%s://%s:%i/%c%s\" from %s", - (st.server_port == st.server_tls_port ? "s" : ""), - st.server_host, - st.server_port, - st.req_filetype, - st.req_selector, - st.req_remote_addr); - } + log_info("request for \"gopher%s://%s:%i/%c%s\" from %s", + st.server_port == st.server_tls_port ? "s" : "", + st.server_host, + st.server_port, + st.req_filetype, + st.req_selector, + st.req_remote_addr); /* Check file type & act accordingly */ switch (file.st_mode & S_IFMT) { diff --git a/src/gophernicus.h b/src/gophernicus.h index f332db5..1e8ce21 100644 --- a/src/gophernicus.h +++ b/src/gophernicus.h @@ -113,7 +113,6 @@ #include #include #include -#include #include #include #include @@ -250,6 +249,7 @@ size_t strlcat(char *dst, const char *src, size_t siz); /* Strings */ #define SERVER_SOFTWARE "Gophernicus" #define SERVER_SOFTWARE_FULL SERVER_SOFTWARE "/" VERSION " (%s)" +#define PROGNAME "gophernicus" #define HEADER_FORMAT "[%s]" #define FOOTER_FORMAT "Gophered by Gophernicus/" VERSION " on %s" @@ -484,4 +484,12 @@ void update_shm_session(state *st, shm_state *shm); /* options.c */ void add_ftype_mapping(state *st, char *suffix); void parse_args(state *st, int argc, char *argv[]); + +/* log.c */ +void log_init(int enable, int debug); +void log_fatal(const char *fmt, ...); +void log_warning(const char *fmt, ...); +void log_info(const char *fmt, ...); +void log_debug(const char *fmt, ...); + #endif diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..5da10af --- /dev/null +++ b/src/log.c @@ -0,0 +1,88 @@ +#include +#include + +#include "gophernicus.h" + +#if defined(LOG_UPTO) +#define _LOG_UPTO LOG_UPTO +#else +#define _LOG_UPTO(pri) \ + ( \ + ((pri) == LOG_EMERG) ? (LOG_MASK(LOG_EMERG)) : \ + ((pri) == LOG_ALERT) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT)) : \ + ((pri) == LOG_CRIT) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT)) : \ + ((pri) == LOG_ERR) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR)) : \ + ((pri) == LOG_WARNING) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING)) : \ + ((pri) == LOG_NOTICE) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING) | LOG_MASK(LOG_NOTICE)) : \ + ((pri) == LOG_INFO) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING) | LOG_MASK(LOG_NOTICE) | LOG_MASK(LOG_INFO)) : \ + ((pri) == LOG_DEBUG) ? (LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING) | LOG_MASK(LOG_NOTICE) | LOG_MASK(LOG_INFO) | LOG_MASK(LOG_DEBUG)) : \ + 0) +#endif + +static int _enable = 0; + +void log_init(int enable, int debug) +{ + if (!enable) return; + + _enable = enable; + + openlog(PROGNAME, + LOG_PID | (debug ? LOG_PERROR : 0), + LOG_DAEMON); + + setlogmask(_LOG_UPTO(debug ? LOG_DEBUG : LOG_INFO)); +} + +static void _vlog(int priority, const char *fmt, va_list ap) +{ + char buf[BUFSIZE]; + + if (!_enable) return; + + vsnprintf(buf, sizeof(buf), fmt, ap); + + syslog(priority, "%s", buf); +} + +static void _log(int priority, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _vlog(priority, fmt, ap); + va_end(ap); +} + + +void log_fatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if (errno) { + int en = errno; + char buf[BUFSIZE]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + _log(LOG_CRIT, "%s (%s)", buf, strerror(en)); + } else { + _vlog(LOG_CRIT, fmt, ap); + } + va_end(ap); + +} + +#define _GOPHERNICUS_MK_LOG_FUNCTION(FCT_LVL, LOG_LVL) \ +void FCT_LVL(const char *fmt, ...) \ +{ \ + va_list ap; \ + \ + va_start(ap, fmt); \ + _vlog(LOG_LVL, fmt, ap); \ + va_end(ap); \ +} +_GOPHERNICUS_MK_LOG_FUNCTION(log_warning, LOG_WARNING) +_GOPHERNICUS_MK_LOG_FUNCTION(log_info, LOG_INFO) +_GOPHERNICUS_MK_LOG_FUNCTION(log_debug, LOG_DEBUG) +#undef _GOPHERNICUS_MK_LOG_FUNCTION diff --git a/src/menu.c b/src/menu.c index 7d8cf24..9b534cd 100644 --- a/src/menu.c +++ b/src/menu.c @@ -339,16 +339,10 @@ static int gophermap(state *st, char *mapfile, int depth) exe = TRUE; } - /* Debug output */ - if (st->debug) { - if (exe) { - if (st->opt_exec) - syslog(LOG_INFO, "parsing executable gophermap \"%s\"", mapfile); - else - syslog(LOG_INFO, "parsing executable gophermap \"%s\" forbidden by -nx", mapfile); - } - else syslog(LOG_INFO, "parsing static gophermap \"%s\"", mapfile); - } + log_debug("parsing %s gophermap \"%s\"%s", + exe ? "executable" : "static", + mapfile, + exe && !st->opt_exec ? ": forbidden by `-nx'" : ""); /* Try to execute or open the mapfile */ if (exe & st->opt_exec) { diff --git a/src/platform.c b/src/platform.c index 53f7c47..c4f23d5 100644 --- a/src/platform.c +++ b/src/platform.c @@ -280,11 +280,8 @@ void platform(state *st) machine); #endif - /* Debug */ - if (st->debug) { - syslog(LOG_INFO, "generated platform string \"%s\"", - st->server_platform); - } + log_debug("generated platform string \"%s\"", + st->server_platform); #else /* Fallback reply */ diff --git a/src/session.c b/src/session.c index e7fced4..a522560 100644 --- a/src/session.c +++ b/src/session.c @@ -143,8 +143,8 @@ void update_shm_session(state *st, shm_state *shm) shm->session[i].hits / st->session_max_hits); /* Throttle user */ - syslog(LOG_INFO, "throttling user from %s for %i seconds", - st->req_remote_addr, delay); + log_info("throttling user from %s for %i seconds", + st->req_remote_addr, delay); sleep(delay); } }