summaryrefslogtreecommitdiffstats
path: root/src/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xsrc/daemon.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/daemon.c b/src/daemon.c
new file mode 100755
index 000000000..268814798
--- /dev/null
+++ b/src/daemon.c
@@ -0,0 +1,317 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <execinfo.h>
+
+#include "common.h"
+#include "appconfig.h"
+#include "log.h"
+#include "web_client.h"
+#include "plugins_d.h"
+#include "rrd.h"
+#include "popen.h"
+#include "main.h"
+#include "daemon.h"
+
+void sig_handler(int signo)
+{
+ switch(signo) {
+ case SIGILL:
+ case SIGABRT:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGSYS:
+ case SIGTRAP:
+ case SIGXCPU:
+ case SIGXFSZ:
+ infoerr("Death signaled exit (signal %d).", signo);
+ signal(signo, SIG_DFL);
+ break;
+
+ case SIGKILL:
+ case SIGTERM:
+ case SIGQUIT:
+ case SIGINT:
+ case SIGHUP:
+ case SIGUSR1:
+ case SIGUSR2:
+ infoerr("Signaled exit (signal %d).", signo);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+ netdata_cleanup_and_exit(1);
+ break;
+
+ case SIGPIPE:
+ infoerr("Signaled PIPE (signal %d).", signo);
+ // this is received when web clients send a reset
+ // no need to log it.
+ // infoerr("Ignoring signal %d.", signo);
+ break;
+
+ default:
+ info("Signal %d received. Falling back to default action for it.", signo);
+ signal(signo, SIG_DFL);
+ break;
+ }
+}
+
+char rundir[FILENAME_MAX + 1] = "/var/run/netdata";
+char pidfile[FILENAME_MAX + 1] = "";
+void prepare_rundir() {
+ if(getuid() != 0) {
+ mkdir("/run/user", 0775);
+ snprintf(rundir, FILENAME_MAX, "/run/user/%d", getuid());
+ mkdir(rundir, 0775);
+ snprintf(rundir, FILENAME_MAX, "/run/user/%d/netdata", getuid());
+ }
+
+ snprintf(pidfile, FILENAME_MAX, "%s/netdata.pid", rundir);
+
+ if(mkdir(rundir, 0775) != 0) {
+ if(errno != EEXIST) error("Cannot create directory '%s'.", rundir);
+ }
+}
+
+int become_user(const char *username)
+{
+ struct passwd *pw = getpwnam(username);
+ if(!pw) {
+ error("User %s is not present.", username);
+ return -1;
+ }
+
+ if(chown(rundir, pw->pw_uid, pw->pw_gid) != 0) {
+ error("Cannot chown directory '%s' to user %s.", rundir, username);
+ return -1;
+ }
+
+ if(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
+ error("Cannot switch to user's %s group (gid: %d).", username, pw->pw_gid);
+ return -1;
+ }
+
+ if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
+ error("Cannot switch to user %s (uid: %d).", username, pw->pw_uid);
+ return -1;
+ }
+
+ if(setgid(pw->pw_gid) != 0) {
+ error("Cannot switch to user's %s group (gid: %d).", username, pw->pw_gid);
+ return -1;
+ }
+ if(setegid(pw->pw_gid) != 0) {
+ error("Cannot effectively switch to user's %s group (gid: %d).", username, pw->pw_gid);
+ return -1;
+ }
+ if(setuid(pw->pw_uid) != 0) {
+ error("Cannot switch to user %s (uid: %d).", username, pw->pw_uid);
+ return -1;
+ }
+ if(seteuid(pw->pw_uid) != 0) {
+ error("Cannot effectively switch to user %s (uid: %d).", username, pw->pw_uid);
+ return -1;
+ }
+
+ return(0);
+}
+
+int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp)
+{
+ fflush(NULL);
+
+ // open the files before forking
+ int input_fd = -1, output_fd = -1, error_fd = -1, dev_null;
+
+ if(input && *input) {
+ if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
+ error("Cannot open input file '%s'.", input);
+ return -1;
+ }
+ }
+
+ if(output && *output) {
+ if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
+ error("Cannot open output log file '%s'", output);
+ if(input_fd != -1) close(input_fd);
+ return -1;
+ }
+ }
+
+ if(error && *error) {
+ if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
+ error("Cannot open error log file '%s'.", error);
+ if(input_fd != -1) close(input_fd);
+ if(output_fd != -1) close(output_fd);
+ return -1;
+ }
+ }
+
+ if(access && *access && access_fd) {
+ if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
+ error("Cannot open access log file '%s'", access);
+ if(input_fd != -1) close(input_fd);
+ if(output_fd != -1) close(output_fd);
+ if(error_fd != -1) close(error_fd);
+ return -1;
+ }
+
+ if(access_fp) {
+ *access_fp = fdopen(*access_fd, "w");
+ if(!*access_fp) {
+ error("Cannot migrate file's '%s' fd %d.", access, *access_fd);
+ if(input_fd != -1) close(input_fd);
+ if(output_fd != -1) close(output_fd);
+ if(error_fd != -1) close(error_fd);
+ close(*access_fd);
+ *access_fd = -1;
+ return -1;
+ }
+ }
+ }
+
+ if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) {
+ perror("Cannot open /dev/null");
+ if(input_fd != -1) close(input_fd);
+ if(output_fd != -1) close(output_fd);
+ if(error_fd != -1) close(error_fd);
+ if(access && access_fd && *access_fd != -1) {
+ close(*access_fd);
+ *access_fd = -1;
+ if(access_fp) {
+ fclose(*access_fp);
+ *access_fp = NULL;
+ }
+ }
+ return -1;
+ }
+
+ // all files opened
+ // lets do it
+
+ if(!dont_fork) {
+ int i = fork();
+ if(i == -1) {
+ perror("cannot fork");
+ exit(1);
+ }
+ if(i != 0) {
+ exit(0); // the parent
+ }
+
+ // become session leader
+ if (setsid() < 0) {
+ perror("Cannot become session leader.");
+ exit(2);
+ }
+ }
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGWINCH, SIG_IGN);
+
+ // fork() again
+ if(!dont_fork) {
+ int i = fork();
+ if(i == -1) {
+ perror("cannot fork");
+ exit(1);
+ }
+ if(i != 0) {
+ exit(0); // the parent
+ }
+ }
+
+ // Set new file permissions
+ umask(0);
+
+ // close all files
+ if(close_all_files) {
+ int i;
+ for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
+ if(
+ ((access_fd && i != *access_fd) || !access_fd)
+ && i != dev_null
+ && i != input_fd
+ && i != output_fd
+ && i != error_fd
+ && fd_is_valid(i)
+ ) close(i);
+ }
+ else {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ // put the opened files
+ // to our standard file descriptors
+ if(input_fd != -1) {
+ if(input_fd != STDIN_FILENO) {
+ dup2(input_fd, STDIN_FILENO);
+ close(input_fd);
+ }
+ input_fd = -1;
+ }
+ else dup2(dev_null, STDIN_FILENO);
+
+ if(output_fd != -1) {
+ if(output_fd != STDOUT_FILENO) {
+ dup2(output_fd, STDOUT_FILENO);
+ close(output_fd);
+ }
+ output_fd = -1;
+ }
+ else dup2(dev_null, STDOUT_FILENO);
+
+ if(error_fd != -1) {
+ if(error_fd != STDERR_FILENO) {
+ dup2(error_fd, STDERR_FILENO);
+ close(error_fd);
+ }
+ error_fd = -1;
+ }
+ else dup2(dev_null, STDERR_FILENO);
+
+ // close /dev/null
+ if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
+ close(dev_null);
+
+ // generate our pid file
+ {
+ unlink(pidfile);
+ int fd = open(pidfile, O_RDWR | O_CREAT, 0666);
+ if(fd >= 0) {
+ char b[100];
+ sprintf(b, "%d\n", getpid());
+ ssize_t i = write(fd, b, strlen(b));
+ if(i <= 0) perror("Cannot write pid to file.");
+ close(fd);
+ }
+ }
+
+ if(user && *user) {
+ if(become_user(user) != 0) {
+ error("Cannot become user '%s'. Continuing as we are.", user);
+ }
+ else info("Successfully became user '%s'.", user);
+ }
+
+ return(0);
+}