Simplify daemon and always close file descriptors
Use daemon(3) to daemonise rather than writing our own code. Since we use the mode of daemon(3) which closes the standard 3 file descriptors, if the user requests logging to one of those we dup(2) the original first. We must also take care to always close any newly-opened file descriptors before execve(2) of the child.
This commit is contained in:
parent
c55693ce1c
commit
639944b905
|
@ -1,8 +1,8 @@
|
||||||
/* daemonitor/src/daemonitor/200_log.c
|
/* daemonitor/src/daemonitor/200_log.c
|
||||||
*
|
*
|
||||||
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
|
* Copyright: ©2007–2012, Laurence Withers.
|
||||||
* Released under the GNU GPLv3. See file COPYING or
|
* Author: Laurence Withers <l@lwithers.me.uk>
|
||||||
* http://www.gnu.org/copyleft/gpl.html for details.
|
* License: GPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,16 +170,25 @@ void log_func_syslog(int level, const char* fmt, ...)
|
||||||
/* log_destination_set()
|
/* log_destination_set()
|
||||||
* Set `log_func' function pointer.
|
* Set `log_func' function pointer.
|
||||||
*/
|
*/
|
||||||
void log_destination_set(enum log_destination_t dest)
|
void
|
||||||
|
log_destination_set(enum log_destination_t dest)
|
||||||
{
|
{
|
||||||
switch(dest) {
|
switch(dest) {
|
||||||
case log_destination_stdout:
|
case log_destination_stdout:
|
||||||
log_func_fd = STDOUT_FILENO;
|
log_func_fd = dup(STDOUT_FILENO);
|
||||||
|
if(log_func_fd == -1) {
|
||||||
|
printf("dup() failed: %m.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
log_func = log_func_file;
|
log_func = log_func_file;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case log_destination_stderr:
|
case log_destination_stderr:
|
||||||
log_func_fd = STDERR_FILENO;
|
log_func_fd = dup(STDERR_FILENO);
|
||||||
|
if(log_func_fd == -1) {
|
||||||
|
fprintf(stderr, "dup() failed: %m.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
log_func = log_func_file;
|
log_func = log_func_file;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -210,6 +219,5 @@ int log_destination_set_file(const char* filename)
|
||||||
|
|
||||||
|
|
||||||
/* options for text editors
|
/* options for text editors
|
||||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
|
||||||
vim: expandtab:ts=4:sw=4
|
vim: expandtab:ts=4:sw=4
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,160 +0,0 @@
|
||||||
/* daemonitor/src/daemonitor/400_daemon.c
|
|
||||||
*
|
|
||||||
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
|
|
||||||
* Released under the GNU GPLv3. See file COPYING or
|
|
||||||
* http://www.gnu.org/copyleft/gpl.html for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* DAEMON ROUTINES ********************************************************************************
|
|
||||||
*
|
|
||||||
* This file provides the daemonise() routine, which makes the process a daemon (fork() + setsid()
|
|
||||||
* so that the process lives in the background, also opens /dev/console). It also provides the
|
|
||||||
* close_file_descriptors() routine, which clears all file descriptors but 0, 1 and 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* PUBLIC API ************************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* daemonise()
|
|
||||||
* Performs a fork() and setsid() so that the process lives in the background. It closes stdin,
|
|
||||||
* stdout and stderr and opens /dev/null and/or /dev/console instead, as appropriate. On error,
|
|
||||||
* this function exits the program.
|
|
||||||
*/
|
|
||||||
void daemonise(void);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* close_file_descriptors()
|
|
||||||
* Closes all file descriptors but stdin, stdout and stderr. Should be called whether we are
|
|
||||||
* daemonising or not.
|
|
||||||
*/
|
|
||||||
void close_file_descriptors(void);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* IMPLEMENTATION ********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void daemonise(void)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
/* close stdin and reopen it as /dev/null */
|
|
||||||
TEMP_FAILURE_RETRY( close(STDIN_FILENO) );
|
|
||||||
fd = open("/dev/null", O_RDONLY);
|
|
||||||
switch(fd) {
|
|
||||||
case -1:
|
|
||||||
LOG(LOG_CRIT, "Could not open `/dev/null' (%m).");
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
case STDIN_FILENO:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG(LOG_CRIT, "Opened `/dev/null' for stdin, but got file descriptor %d instead.", fd);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close stdout and reopen it as /dev/console if possible, or /dev/null if not */
|
|
||||||
TEMP_FAILURE_RETRY( close(STDOUT_FILENO) );
|
|
||||||
fd = open("/dev/console", O_WRONLY | O_NOCTTY);
|
|
||||||
if(fd == -1) {
|
|
||||||
LOG(LOG_WARNING, "Could not open `/dev/console' for stdout (%m).");
|
|
||||||
fd = open("/dev/null", O_WRONLY);
|
|
||||||
}
|
|
||||||
switch(fd) {
|
|
||||||
case -1:
|
|
||||||
LOG(LOG_CRIT, "Could not open `/dev/null' (%m).");
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
case STDOUT_FILENO:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG(LOG_CRIT, "Opened `/dev/null' for stdout, but got file descriptor %d instead.", fd);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close stderr and reopen it as /dev/console if possible, or /dev/null if not */
|
|
||||||
TEMP_FAILURE_RETRY( close(STDERR_FILENO) );
|
|
||||||
fd = open("/dev/console", O_WRONLY | O_NOCTTY);
|
|
||||||
if(fd == -1) {
|
|
||||||
LOG(LOG_WARNING, "Could not open `/dev/console' for stderr (%m).");
|
|
||||||
/* force fd closed again, in case glibc kindly connected it to syslog for us */
|
|
||||||
TEMP_FAILURE_RETRY( close(STDERR_FILENO) );
|
|
||||||
fd = open("/dev/null", O_WRONLY);
|
|
||||||
}
|
|
||||||
switch(fd) {
|
|
||||||
case -1:
|
|
||||||
LOG(LOG_CRIT, "Could not open `/dev/null' (%m).");
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
case STDERR_FILENO:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG(LOG_CRIT, "Opened `/dev/null' for stderr, but got file descriptor %d instead.", fd);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* daemonise */
|
|
||||||
pid = fork();
|
|
||||||
if(pid == (pid_t)-1) {
|
|
||||||
LOG(LOG_CRIT, "Could not fork daemonised process (%m).");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pid) {
|
|
||||||
/* parent */
|
|
||||||
_exit(0); /* don't call atexit() et al. in parent */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setsid();
|
|
||||||
LOG(LOG_NOTICE, "Daemonised, PID %lu.", (unsigned long)getpid());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* close_file_descriptors()
|
|
||||||
* Adapted from libgslutil (GPL-3).
|
|
||||||
*/
|
|
||||||
void close_file_descriptors(void)
|
|
||||||
{
|
|
||||||
DIR* d;
|
|
||||||
struct dirent* de;
|
|
||||||
int i, end, dfd;
|
|
||||||
|
|
||||||
d = opendir("/proc/self/fd");
|
|
||||||
if(!d) {
|
|
||||||
/* backup method */
|
|
||||||
for(i = STDERR_FILENO + 1, end = getdtablesize(); i < end; ++i) {
|
|
||||||
close(i);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dfd = dirfd(d);
|
|
||||||
|
|
||||||
while( (de = readdir(d)) ) {
|
|
||||||
if(de->d_name[0] == '.') continue;
|
|
||||||
i = strtol(de->d_name, 0, 10);
|
|
||||||
if(i == dfd) continue; /* skip file descriptor associated with `DIR* d' */
|
|
||||||
if(i <= STDERR_FILENO) continue; /* leave stdin, stdout, stderr alone */
|
|
||||||
TEMP_FAILURE_RETRY( close(i) );
|
|
||||||
}
|
|
||||||
closedir(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* options for text editors
|
|
||||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
|
||||||
vim: expandtab:ts=4:sw=4
|
|
||||||
*/
|
|
|
@ -1,8 +1,8 @@
|
||||||
/* daemonitor/src/daemonitor/600_run_program.c
|
/* daemonitor/src/daemonitor/600_run_program.c
|
||||||
*
|
*
|
||||||
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
|
* Copyright: ©2007–2012, Laurence Withers.
|
||||||
* Released under the GNU GPLv3. See file COPYING or
|
* Author: Laurence Withers <l@lwithers.me.uk>
|
||||||
* http://www.gnu.org/copyleft/gpl.html for details.
|
* License: GPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,8 +45,8 @@ volatile int sigterm_received = 0;
|
||||||
|
|
||||||
|
|
||||||
/* close_file_descriptors()
|
/* close_file_descriptors()
|
||||||
* Closes all file descriptors but stdin, stdout and stderr. Should be called whether we are
|
* Closes all file descriptors but stdin, stdout and stderr (which it leaves
|
||||||
* daemonising or not.
|
* unchanged).
|
||||||
*/
|
*/
|
||||||
void close_file_descriptors(void);
|
void close_file_descriptors(void);
|
||||||
|
|
||||||
|
@ -56,6 +56,38 @@ void close_file_descriptors(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* close_file_descriptors()
|
||||||
|
* Adapted from libgslutil (GPL-3).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
close_file_descriptors(void)
|
||||||
|
{
|
||||||
|
DIR* d;
|
||||||
|
struct dirent* de;
|
||||||
|
int i, end, dfd;
|
||||||
|
|
||||||
|
d = opendir("/proc/self/fd");
|
||||||
|
if(!d) {
|
||||||
|
/* backup method */
|
||||||
|
for(i = STDERR_FILENO + 1, end = getdtablesize(); i < end; ++i) {
|
||||||
|
close(i);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dfd = dirfd(d);
|
||||||
|
|
||||||
|
while( (de = readdir(d)) ) {
|
||||||
|
if(de->d_name[0] == '.') continue;
|
||||||
|
i = strtol(de->d_name, 0, 10);
|
||||||
|
if(i == dfd) continue; /* skip file descriptor associated with `DIR* d' */
|
||||||
|
if(i <= STDERR_FILENO) continue; /* leave stdin, stdout, stderr alone */
|
||||||
|
TEMP_FAILURE_RETRY( close(i) );
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* crash()
|
/* crash()
|
||||||
* Crash the program. This is better than just calling exit() because it doesn't clean up. We
|
* Crash the program. This is better than just calling exit() because it doesn't clean up. We
|
||||||
* should leave our PID file lying around to look like we crashed. This function is paranoid.
|
* should leave our PID file lying around to look like we crashed. This function is paranoid.
|
||||||
|
@ -113,6 +145,8 @@ void run_program(const char* exe_path, char** argv)
|
||||||
|
|
||||||
/* child -- execute new process */
|
/* child -- execute new process */
|
||||||
if(pid == 0) {
|
if(pid == 0) {
|
||||||
|
close_file_descriptors();
|
||||||
|
|
||||||
/* clear the child's signal mask */
|
/* clear the child's signal mask */
|
||||||
sigemptyset(&ss);
|
sigemptyset(&ss);
|
||||||
sigprocmask(SIG_SETMASK, &ss, 0);
|
sigprocmask(SIG_SETMASK, &ss, 0);
|
||||||
|
@ -203,6 +237,5 @@ void run_program(const char* exe_path, char** argv)
|
||||||
|
|
||||||
|
|
||||||
/* options for text editors
|
/* options for text editors
|
||||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
|
||||||
vim: expandtab:ts=4:sw=4
|
vim: expandtab:ts=4:sw=4
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -79,7 +79,7 @@ int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
char* endp;
|
char* endp;
|
||||||
const char* log_argument = 0, * pidfile_argument = 0, * restart_argument = 0;
|
const char* log_argument = 0, * pidfile_argument = 0, * restart_argument = 0;
|
||||||
int daemon = 0, restart_interval = DEFAULT_RESTART_INTERVAL;
|
int do_daemon = 0, restart_interval = DEFAULT_RESTART_INTERVAL;
|
||||||
|
|
||||||
/* safety */
|
/* safety */
|
||||||
close_file_descriptors(); /* leaves stdout etc. open */
|
close_file_descriptors(); /* leaves stdout etc. open */
|
||||||
|
@ -91,7 +91,7 @@ int main(int argc, char* argv[])
|
||||||
case '?': LOG_ANYWHERE("Invalid commandline options."); return 1;
|
case '?': LOG_ANYWHERE("Invalid commandline options."); return 1;
|
||||||
case 'h': usage(); return 1;
|
case 'h': usage(); return 1;
|
||||||
case 'V': fputs("daemonitor " VERSION "\n", stderr); return 1;
|
case 'V': fputs("daemonitor " VERSION "\n", stderr); return 1;
|
||||||
case 'd': ++daemon; break;
|
case 'd': ++do_daemon; break;
|
||||||
case 'l': log_argument = optarg; break;
|
case 'l': log_argument = optarg; break;
|
||||||
case 'p': pidfile_argument = optarg; break;
|
case 'p': pidfile_argument = optarg; break;
|
||||||
case 'R': restart_argument = optarg; break;
|
case 'R': restart_argument = optarg; break;
|
||||||
|
@ -136,7 +136,12 @@ int main(int argc, char* argv[])
|
||||||
if(load_child_environ()) return 1;
|
if(load_child_environ()) return 1;
|
||||||
|
|
||||||
/* daemonise? */
|
/* daemonise? */
|
||||||
if(daemon) daemonise();
|
if(do_daemon) {
|
||||||
|
if(daemon(0, 0)) {
|
||||||
|
LOG(LOG_CRIT, "daemon() failed: %m.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* write a PID file */
|
/* write a PID file */
|
||||||
if(pidfile_argument) {
|
if(pidfile_argument) {
|
||||||
|
|
Loading…
Reference in New Issue