From c77fb9d8f09d136fa71bde2469c4fd11cefa6f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= Date: Sun, 16 Apr 2017 20:20:08 +0100 Subject: [PATCH] Capture and log STDOUT and STDERR output from dhcp-script. --- CHANGELOG | 7 ++++++- man/dnsmasq.8 | 4 ++-- src/dnsmasq.c | 8 ++++++++ src/dnsmasq.h | 54 +++++++++++++++++++++++++------------------------ src/helper.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/log.c | 4 +++- 6 files changed, 100 insertions(+), 33 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c72ab06..dd19ead 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -88,7 +88,12 @@ version 2.77 Add mtu setting facility to --ra-param. Thanks to David Flamand for the patch. - + + Capture STDOUT and STDERR output from dhcp-script and log + it as part of the dnsmasq log stream. Makes life easier + for diagnosing unexpected problems in scripts. + Thanks to Petr Mensik for the patch. + version 2.76 Include 0.0.0.0/8 in DNS rebind checks. This range diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index d231e7b..6c9ebaf 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1577,8 +1577,8 @@ database. All file descriptors are -closed except stdin, stdout and stderr which are open to /dev/null -(except in debug mode). +closed except stdin, which is open to /dev/null, and stdout and stderr which capture output for logging by dnsmasq. +(In debug mode, stdio, stdout and stderr file are left as those inherited from the invoker of dnsmasq). The script is not invoked concurrently: at most one instance of the script is ever running (dnsmasq waits for an instance of script to exit diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 70b84dc..045489f 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -1302,6 +1302,7 @@ static void async_event(int pipe, time_t now) daemon->tcp_pids[i] = 0; break; +#if defined(HAVE_SCRIPT) case EVENT_KILLED: my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data); break; @@ -1315,12 +1316,19 @@ static void async_event(int pipe, time_t now) daemon->lease_change_command, strerror(ev.data)); break; + case EVENT_SCRIPT_LOG: + my_syslog(MS_SCRIPT | LOG_DEBUG, "%s", msg ? msg : ""); + free(msg); + msg = NULL; + break; + /* necessary for fatal errors in helper */ case EVENT_USER_ERR: case EVENT_DIE: case EVENT_LUA_ERR: fatal_event(&ev, msg); break; +#endif case EVENT_REOPEN: /* Note: this may leave TCP-handling processes with the old file still open. diff --git a/src/dnsmasq.h b/src/dnsmasq.h index c7d9982..c75ff2e 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -145,30 +145,31 @@ struct event_desc { int event, data, msg_sz; }; -#define EVENT_RELOAD 1 -#define EVENT_DUMP 2 -#define EVENT_ALARM 3 -#define EVENT_TERM 4 -#define EVENT_CHILD 5 -#define EVENT_REOPEN 6 -#define EVENT_EXITED 7 -#define EVENT_KILLED 8 -#define EVENT_EXEC_ERR 9 -#define EVENT_PIPE_ERR 10 -#define EVENT_USER_ERR 11 -#define EVENT_CAP_ERR 12 -#define EVENT_PIDFILE 13 -#define EVENT_HUSER_ERR 14 -#define EVENT_GROUP_ERR 15 -#define EVENT_DIE 16 -#define EVENT_LOG_ERR 17 -#define EVENT_FORK_ERR 18 -#define EVENT_LUA_ERR 19 -#define EVENT_TFTP_ERR 20 -#define EVENT_INIT 21 -#define EVENT_NEWADDR 22 -#define EVENT_NEWROUTE 23 -#define EVENT_TIME_ERR 24 +#define EVENT_RELOAD 1 +#define EVENT_DUMP 2 +#define EVENT_ALARM 3 +#define EVENT_TERM 4 +#define EVENT_CHILD 5 +#define EVENT_REOPEN 6 +#define EVENT_EXITED 7 +#define EVENT_KILLED 8 +#define EVENT_EXEC_ERR 9 +#define EVENT_PIPE_ERR 10 +#define EVENT_USER_ERR 11 +#define EVENT_CAP_ERR 12 +#define EVENT_PIDFILE 13 +#define EVENT_HUSER_ERR 14 +#define EVENT_GROUP_ERR 15 +#define EVENT_DIE 16 +#define EVENT_LOG_ERR 17 +#define EVENT_FORK_ERR 18 +#define EVENT_LUA_ERR 19 +#define EVENT_TFTP_ERR 20 +#define EVENT_INIT 21 +#define EVENT_NEWADDR 22 +#define EVENT_NEWROUTE 23 +#define EVENT_TIME_ERR 24 +#define EVENT_SCRIPT_LOG 25 /* Exit codes. */ #define EC_GOOD 0 @@ -243,8 +244,9 @@ struct event_desc { /* extra flags for my_syslog, we use a couple of facilities since they are known not to occupy the same bits as priorities, no matter how syslog.h is set up. */ -#define MS_TFTP LOG_USER -#define MS_DHCP LOG_DAEMON +#define MS_TFTP LOG_USER +#define MS_DHCP LOG_DAEMON +#define MS_SCRIPT LOG_MAIL struct all_addr { union { diff --git a/src/helper.c b/src/helper.c index 2b8164b..4fffa27 100644 --- a/src/helper.c +++ b/src/helper.c @@ -14,6 +14,7 @@ along with this program. If not, see . */ +#include #include "dnsmasq.h" #ifdef HAVE_SCRIPT @@ -135,7 +136,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd && max_fd != err_fd) close(max_fd); - + #ifdef HAVE_LUASCRIPT if (daemon->luascript) { @@ -189,6 +190,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) unsigned char *buf = (unsigned char *)daemon->namebuff; unsigned char *end, *extradata, *alloc_buff = NULL; int is6, err = 0; + int pipeout[2]; free(alloc_buff); @@ -472,16 +474,54 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) if (!daemon->lease_change_command) continue; + /* Pipe to capture stdout and stderr from script */ + if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1) + continue; + /* possible fork errors are all temporary resource problems */ while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM)) sleep(2); if (pid == -1) - continue; + { + if (!option_bool(OPT_DEBUG)) + { + close(pipeout[0]); + close(pipeout[1]); + } + continue; + } /* wait for child to complete */ if (pid != 0) { + if (!option_bool(OPT_DEBUG)) + { + FILE *fp; + + close(pipeout[1]); + + /* Read lines sent to stdout/err by the script and pass them back to be logged */ + if (!(fp = fdopen(pipeout[0], "r"))) + close(pipeout[0]); + else + { + while (fgets(daemon->packet, daemon->packet_buff_sz, fp)) + { + /* do not include new lines, log will append them */ + size_t len = strlen(daemon->packet); + if (len > 0) + { + --len; + if (daemon->packet[len] == '\n') + daemon->packet[len] = 0; + } + send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet); + } + fclose(fp); + } + } + /* reap our children's children, if necessary */ while (1) { @@ -504,6 +544,15 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) continue; } + + if (!option_bool(OPT_DEBUG)) + { + /* map stdout/stderr of script to pipeout */ + close(pipeout[0]); + dup2(pipeout[1], STDOUT_FILENO); + dup2(pipeout[1], STDERR_FILENO); + close(pipeout[1]); + } if (data.action != ACTION_TFTP && data.action != ACTION_ARP) { @@ -580,7 +629,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) hostname = NULL; my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); - } + } + /* we need to have the event_fd around if exec fails */ if ((i = fcntl(event_fd, F_GETFD)) != -1) fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); diff --git a/src/log.c b/src/log.c index abae78c..5d085a3 100644 --- a/src/log.c +++ b/src/log.c @@ -288,7 +288,9 @@ void my_syslog(int priority, const char *format, ...) func = "-tftp"; else if ((LOG_FACMASK & priority) == MS_DHCP) func = "-dhcp"; - + else if ((LOG_FACMASK & priority) == MS_SCRIPT) + func = "-script"; + #ifdef LOG_PRI priority = LOG_PRI(priority); #else