1
/* Copyright (c) 2003-2008 Sam Trenholme
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
15
* This software is provided 'as is' with no guarantees of correctness or
16
* fitness for purpose.
19
/* I would like to thank D. Richard Felker III for his invaluable help
20
with debugging how the pipes are set up here */
22
/* This program is a helper application which does the following:
24
It starts the maradns process
26
It syslog()s maradns' output via a chroot'd unprivledged child process
28
If maradns exits with a code of 8, this means that she was given a
29
HUP signal. Restart the MaraDNS process
31
If this process gets a HUP signal, restart the MaraDNS process
40
#include <sys/types.h>
44
/* The UID that the Duende logging process uses. CHANGE THE DUENDE MAN
45
PAGE IF YOU CHANGE THIS VALUE (same general process as changing the
46
mararc man page; the source file for the duende man page is duende.ej) */
47
#define DUENDE_LOGGER_UID 66
49
/* The directory that Duende runs in. This directory has to exist for
50
Duende to be able to run. Again, IF YOU CHANGE THIS, CHANGE THE
52
#define DUENDE_CHROOT_DIR "/etc/maradns/logger"
54
int got_hup_signal = 0;
55
int got_term_signal = 0;
57
/* If we get a HUP signal set the flag so we can restart the MaraDNS child
69
/* Helper process which syslogs stuff from either MaraDNS' stdout or stderr. */
71
void log_helper(char *name,int stdout_fd) {
74
/* We can't use our signal handlers because fgets is blocking */
75
signal(SIGTERM,SIG_DFL);
76
signal(SIGHUP,SIG_DFL);
78
/* Open up the sys log */
79
openlog(name,0,LOG_DAEMON);
81
/* Drop all privileges */
82
if(chdir(DUENDE_CHROOT_DIR) != 0) {
83
syslog(LOG_ALERT,"Can not enter chroot directory %s",DUENDE_CHROOT_DIR);
84
syslog(LOG_ALERT,"%s","We can not log daemon output");
85
printf("Fatal error logging; read syslog\n");
86
printf("%s directory required to exist\n",DUENDE_CHROOT_DIR);
89
if(setuid(DUENDE_LOGGER_UID) != 0) {
90
syslog(LOG_ALERT,"%s%d","Can not change UID to ",DUENDE_LOGGER_UID);
91
syslog(LOG_ALERT,"%s","We can not log daemon output");
92
printf("Fatal error logging; read syslog\n");
96
/* Log both stdout and stderr */
99
if(fgets(out_buf,1020,stdin) == out_buf)
101
/* FreeBSD doesn't log daemon.info messages by default; while
102
* this can be changed by editing /etc/syslog.conf, it *is*
103
* an issue that can cause confusion */
104
syslog(LOG_ALERT,"%s",out_buf);
105
#else /* __FreeBSD__ */
106
syslog(LOG_INFO,"%s",out_buf);
107
#endif /* __FreeBSD__ */
110
syslog(LOG_ALERT,"log_helper process terminating");
116
/* If a child exits, see if the child exited with a code of 8 or received
117
a HUP signal. In either of these cases, restart the child daemon and the
118
(if needed) logger process). Otherwise, exit */
120
void handle_child_exited(int exit_status, pid_t alive, pid_t exited) {
121
if(WIFEXITED(exit_status)) { /* Exit with exit code */
122
if(WEXITSTATUS(exit_status) != 8) { /* Anything but HUP */
124
syslog(LOG_ALERT,"Child exited with status %d",exit_status);
125
exit(WEXITSTATUS(exit_status));
128
if(WIFSIGNALED(exit_status)) { /* Got signal */
129
if(WTERMSIG(exit_status) != SIGHUP) {
130
syslog(LOG_ALERT,"Child got signal %d",exit_status);
135
/* If you somehow stop the child daemon, we go bye bye */
136
if(WIFSTOPPED(exit_status)) {
137
syslog(LOG_ALERT,"Child stopped");
138
kill(exited,SIGTERM);
142
/* Clean up the system logging */
143
syslog(LOG_ALERT,"Cleaning up system logging");
147
/* The main process forks off the child. Right now, I will just have
148
it fork off the MaraDNS process, hardwired as /usr/sbin/maradns,
149
directing her standard output to
150
/dev/null. The revision of this file will correctly handle Mara's
154
int main(int argc, char **argv) {
157
int stream1[2]; /* Used for piping */
158
int exec_argv_offset = 1; /* Also used to determine PID writing */
159
if(argv[0] == NULL || argv[1] == NULL) {
160
printf("Usage: duende (--pid=/path/to/file) [program] [arguments]\n");
163
if(!strncasecmp(argv[1],"--pid=",6)) {
164
if(argv[2] == NULL) {
166
"Usage: duende (--pid=/path/to/file) [program] [arguments]\n");
169
exec_argv_offset = 2;
172
/* Let children know that duende is running */
173
if(setenv("DUENDE_IS_RUNNING","1",0) != 0) {
174
printf("FATAL: Unable to set environment variable\n");
178
/* The parent immediately exits */
182
/* The child becomes a full-fledged daemon */
183
setpgid(0,0); /* No longer visible in 'ps' without the 'auxw' argument */
185
/* Write our PID to a file if the user so desires us to */
186
if(exec_argv_offset == 2) {
187
FILE *fp_pid = fopen(argv[1] + 6,"w");
189
syslog(LOG_ALERT,"Fatal writing, to PID file, error\n");
192
unsigned int local_pid = getpid();
193
fprintf(fp_pid,"%u",local_pid);
197
/* Sysadmins expect HUP to reload, so we set that up */
198
signal(SIGHUP,handle_hup);
199
signal(SIGTERM,handle_term);
200
signal(SIGINT,handle_term);
202
pid = 0; log_pid = 0;
205
if(pipe(stream1) != 0) {
206
syslog(LOG_ALERT,"Fatal pipe error");
211
syslog(LOG_ALERT,"Fatal pid error");
214
if(pid == 0) { /* Child; this one execs maradns */
216
/* Dup the standard output */
217
if(dup2(stream1[1],1) != 1) {
218
syslog(LOG_ALERT,"Fatal dup2 error 1");
221
/* And the standard error */
222
if(dup2(stream1[1],2) != 2) {
223
syslog(LOG_ALERT,"Fatal dup2 error 2");
226
argv[0] = argv[exec_argv_offset];
227
execvp(argv[exec_argv_offset],argv + exec_argv_offset);
229
printf("duende: %s: Command can't run, terminating\n",argv[exec_argv_offset]);
230
syslog(LOG_ALERT,"Command can't run, terminating\n");
237
if(log_pid == 0) { /* Child to syslog all of MaraDNS' output */
238
argv[0] = "duende-log-helper";
239
log_helper(argv[exec_argv_offset],stream1[0]);
240
syslog(LOG_ALERT,"log_helper finished, terminating\n");
244
/* If we got a HUP signal, send it to the child */
245
if(got_hup_signal == 1) {
249
/* If we got a TERM or INT signal, send it to the children
250
then exit ourselves */
251
else if(got_term_signal == 1) {
252
/* XXX: make sure term really stops the children */
254
kill(log_pid,SIGTERM);
255
syslog(LOG_ALERT,"got term signal, terminating\n");
259
if(waitpid(pid,&exit_status,WNOHANG) == pid) { /* If child ended */
260
handle_child_exited(exit_status,log_pid,pid);
262
break; /* Out of the inner loop; re-start Mara */
264
/* If logger terminated */
265
if(waitpid(log_pid,&exit_status,WNOHANG) == log_pid) {
266
handle_child_exited(exit_status,pid,log_pid);
268
break; /* Out of the inner loop; re-start Mara */