void sig_handler_exit(int signo)
{
- if(signo) {
- error_log_limit_unlimited();
- error("Received signal %d. Exiting...", signo);
- netdata_exit = 1;
- }
+ if(signo) {
+ error_log_limit_unlimited();
+ error("Received signal %d. Exiting...", signo);
+ netdata_exit = 1;
+ }
+}
+
+void sig_handler_logrotate(int signo)
+{
+ if(signo) {
+ error_log_limit_reset();
+ info("Received signal %d to re-open the log files", signo);
+ reopen_all_log_files();
+ }
}
void sig_handler_save(int signo)
{
if(signo) {
+ error_log_limit_reset();
info("Received signal %d to save the database...", signo);
rrdset_save_all();
}
}
-static void properly_chown_netdata_generated_file(int fd, uid_t uid, gid_t gid) {
+static void chown_open_file(int fd, uid_t uid, gid_t gid) {
if(fd == -1) return;
struct stat buf;
}
}
-int become_user(const char *username, int access_fd, int output_fd, int error_fd, int pid_fd)
+int become_user(const char *username, int pid_fd)
{
struct passwd *pw = getpwnam(username);
if(!pw) {
}
}
- properly_chown_netdata_generated_file(access_fd, uid, gid);
- properly_chown_netdata_generated_file(output_fd, uid, gid);
- properly_chown_netdata_generated_file(error_fd, uid, gid);
- properly_chown_netdata_generated_file(pid_fd, uid, gid);
+ chown_open_file(STDOUT_FILENO, uid, gid);
+ chown_open_file(STDERR_FILENO, uid, gid);
+ chown_open_file(stdaccess_fd, uid, gid);
+ chown_open_file(pid_fd, uid, gid);
if(supplementary_groups && ngroups) {
if(setgroups(ngroups, supplementary_groups) == -1)
return i;
}
-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)
+int become_daemon(int dont_fork, const char *user)
{
- 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 && strcmp(output, "/dev/null") != 0) {
- 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 && strcmp(error, "/dev/null") != 0) {
- 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 && strcmp(access, "/dev/null") != 0) {
- 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(setvbuf(*access_fp, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on access.log");
- }
- }
-
- 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) {
}
}
- // 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);
- }
-
- if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on debug.log");
-
- output_fd = STDOUT_FILENO;
- }
- else dup2(dev_null, STDOUT_FILENO);
-
- if(error_fd != -1) {
- if(error_fd != STDERR_FILENO) {
- dup2(error_fd, STDERR_FILENO);
- close(error_fd);
- }
-
- if(setvbuf(stderr, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on error.log");
-
- error_fd = STDERR_FILENO;
- }
- 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
int pidfd = -1;
if(pidfile[0]) {
- pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
+ pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
if(pidfd >= 0) {
if(ftruncate(pidfd, 0) != 0)
error("Cannot truncate pidfile '%s'.", pidfile);
}
if(user && *user) {
- if(become_user(user, (access_fd)?*access_fd:-1, output_fd, error_fd, pidfd) != 0) {
+ if(become_user(user, pidfd) != 0) {
error("Cannot become user '%s'. Continuing as we are.", user);
}
else info("Successfully became user '%s'.", user);
extern void sig_handler_exit(int signo);
extern void sig_handler_save(int signo);
+extern void sig_handler_logrotate(int signo);
-extern int become_user(const char *username, int access_fd, int output_fd, int error_fd, int pid_fd);
+extern int become_user(const char *username, int pid_fd);
-extern 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);
+extern int become_daemon(int dont_fork, const char *user);
extern void netdata_cleanup_and_exit(int i);
#include "common.h"
-// ----------------------------------------------------------------------------
-// LOG
-
const char *program_name = "";
unsigned long long debug_flags = DEBUG;
-int silent = 0;
-
-int access_fd = -1;
-FILE *stdaccess = NULL;
-
int access_log_syslog = 1;
int error_log_syslog = 1;
int output_log_syslog = 1; // debug log
+int stdaccess_fd = -1;
+FILE *stdaccess = NULL;
+
+const char *stdaccess_filename = NULL;
+const char *stderr_filename = NULL;
+const char *stdout_filename = NULL;
+
+void syslog_init(void) {
+ static int i = 0;
+
+ if(!i) {
+ openlog(program_name, LOG_PID, LOG_DAEMON);
+ i = 1;
+ }
+}
+
+int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog) {
+ int f, t;
+
+ if(!filename || !*filename || !strcmp(filename, "none"))
+ filename = "/dev/null";
+
+ if(!strcmp(filename, "syslog")) {
+ filename = "/dev/null";
+ syslog_init();
+ if(enabled_syslog) *enabled_syslog = 1;
+ }
+ else if(enabled_syslog) *enabled_syslog = 0;
+
+ // don't do anything if the user is willing
+ // to have the standard one
+ if(!strcmp(filename, "system"))
+ return fd;
+
+ if(!strcmp(filename, "stdout"))
+ f = STDOUT_FILENO;
+
+ else if(!strcmp(filename, "stderr"))
+ f = STDERR_FILENO;
+
+ else {
+ f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
+ if(f == -1) {
+ error("Cannot open file '%s'. Leaving %d to its default.", filename, fd);
+ return fd;
+ }
+ }
+
+ // if there is a level-2 file pointer
+ // flush it before switching the level-1 fds
+ if(fp && *fp)
+ fflush(*fp);
+
+ if(fd != f && fd != -1) {
+ // it automatically closes
+ t = dup2(f, fd);
+ if (t == -1) {
+ error("Cannot dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
+ close(f);
+ return fd;
+ }
+ // info("dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
+ close(f);
+ }
+ else fd = f;
+
+ if(fp && *fp == NULL) {
+ // info("fdopen(%d) on filename '%s'", fd, filename);
+
+ FILE *n = fdopen(fd, "a");
+ if (!n)
+ error("Cannot fdopen() fd %d ('%s')", fd, filename);
+
+ else {
+ if (setvbuf(n, NULL, _IOLBF, 0) != 0)
+ error("Cannot set line buffering on fd %d ('%s')", fd, filename);
+
+ if(!*fp)
+ *fp = n;
+ else {
+ FILE *o = *fp;
+ *fp = n;
+ fclose(o);
+ }
+ }
+ }
+
+ return fd;
+}
+
+void reopen_all_log_files() {
+ if(stdout_filename)
+ open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+
+ if(stderr_filename)
+ open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+
+ if(stdaccess_filename)
+ stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+}
+
+void open_all_log_files() {
+ // disable stdin
+ open_log_file(STDIN_FILENO, &stdin, "/dev/null", NULL);
+
+ open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+ open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+ stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+}
+
+// ----------------------------------------------------------------------------
+// error log throttling
+
time_t error_log_throttle_period = 1200;
unsigned long error_log_errors_per_period = 200;
return 0;
}
+// ----------------------------------------------------------------------------
+// print the date
+
+// FIXME
+// this should print the date in a buffer the way it
+// is now, logs from multiple threads may be multiplexed
+
void log_date(FILE *out)
{
char outstr[24];
fprintf(out, "%s: ", outstr);
}
+// ----------------------------------------------------------------------------
+// debug log
+
void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
va_list args;
fflush(stdout);
}
+// ----------------------------------------------------------------------------
+// info log
+
void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
va_list args;
}
}
+// ----------------------------------------------------------------------------
+// error log
+
void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
va_list args;
netdata_cleanup_and_exit(1);
}
+// ----------------------------------------------------------------------------
+// access log
+
void log_access( const char *fmt, ... )
{
va_list args;
extern const char *program_name;
-extern int silent;
-
-extern int access_fd;
+extern int stdaccess_fd;
extern FILE *stdaccess;
+extern const char *stdaccess_filename;
+extern const char *stderr_filename;
+extern const char *stdout_filename;
+
extern int access_log_syslog;
extern int error_log_syslog;
extern int output_log_syslog;
extern unsigned long error_log_errors_per_period;
extern int error_log_limit(int reset);
+extern void open_all_log_files();
+extern void reopen_all_log_files();
+
#define error_log_limit_reset() do { error_log_limit(1); } while(0)
#define error_log_limit_unlimited() do { error_log_throttle_period = 0; } while(0)
-#define debug(type, args...) do { if(unlikely(!silent && (debug_flags & type))) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
+#define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
#define info(args...) info_int(__FILE__, __FUNCTION__, __LINE__, ##args)
#define infoerr(args...) error_int("INFO", __FILE__, __FUNCTION__, __LINE__, ##args)
#define error(args...) error_int("ERROR", __FILE__, __FUNCTION__, __LINE__, ##args)
// cd to /tmp to avoid any plugins writing files at random places
if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
- char *input_log_file = NULL;
- char *output_log_file = NULL;
- char *error_log_file = NULL;
- char *access_log_file = NULL;
char *user = NULL;
{
char *flags = config_get("global", "debug flags", "0x00000000");
// --------------------------------------------------------------------
-
global_host_prefix = config_get("global", "host access prefix", "");
setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
// --------------------------------------------------------------------
- output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
- if(strcmp(output_log_file, "syslog") == 0) {
- output_log_syslog = 1;
- output_log_file = NULL;
- }
- else if(strcmp(output_log_file, "none") == 0) {
- output_log_syslog = 0;
- output_log_file = NULL;
- }
- else output_log_syslog = 0;
-
- // --------------------------------------------------------------------
-
- error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
- if(strcmp(error_log_file, "syslog") == 0) {
- error_log_syslog = 1;
- error_log_file = NULL;
- }
- else if(strcmp(error_log_file, "none") == 0) {
- error_log_syslog = 0;
- error_log_file = NULL;
- // optimization - do not even generate debug log entries
- }
- else error_log_syslog = 0;
+ stdout_filename = config_get("global", "debug log", LOG_DIR "/debug.log");
+ stderr_filename = config_get("global", "error log", LOG_DIR "/error.log");
+ stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period" , ""), 1);
// --------------------------------------------------------------------
- access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
- if(strcmp(access_log_file, "syslog") == 0) {
- access_log_syslog = 1;
- access_log_file = NULL;
- }
- else if(strcmp(access_log_file, "none") == 0) {
- access_log_syslog = 0;
- access_log_file = NULL;
- }
- else access_log_syslog = 0;
-
- // --------------------------------------------------------------------
-
rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
// --------------------------------------------------------------------
error("Could not block signals for threads");
}
- // Catch signals which we want to use to quit savely
+ // Catch signals which we want to use
struct sigaction sa;
sigemptyset(&sa.sa_mask);
- sigaddset(&sa.sa_mask, SIGHUP);
sigaddset(&sa.sa_mask, SIGINT);
sigaddset(&sa.sa_mask, SIGTERM);
sa.sa_handler = sig_handler_exit;
sa.sa_flags = 0;
- if(sigaction(SIGHUP, &sa, NULL) == -1) {
- error("Failed to change signal handler for SIGHUP");
- }
if(sigaction(SIGINT, &sa, NULL) == -1) {
error("Failed to change signal handler for SIGINT");
}
error("Failed to change signal handler for SIGTERM");
}
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_handler = sig_handler_logrotate;
+ sa.sa_flags = 0;
+ if(sigaction(SIGHUP, &sa, NULL) == -1) {
+ error("Failed to change signal handler for SIGHUP");
+ }
+
// save database on SIGUSR1
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGUSR1);
sa.sa_handler = sig_handler_save;
if(sigaction(SIGUSR1, &sa, NULL) == -1) {
error("Failed to change signal handler for SIGUSR1");
// Ignore SIGPIPE completely.
// INFO: If we add signals here we have to unblock them
// at popen.c when running a external plugin.
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGPIPE);
sa.sa_handler = SIG_IGN;
if(sigaction(SIGPIPE, &sa, NULL) == -1) {
error("Failed to change signal handler for SIGPIPE");
create_listen_sockets();
}
- if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1)
+ // initialize the log files
+ open_all_log_files();
+
+ // become daemon, switch user, create pid file, set process attributes
+ if(become_daemon(dont_fork, user) == -1)
fatal("Cannot demonize myself.");
#ifdef NETDATA_INTERNAL_CHECKS
}
#endif /* NETDATA_INTERNAL_CHECKS */
- if(output_log_syslog || error_log_syslog || access_log_syslog)
- openlog("netdata", LOG_PID, LOG_DAEMON);
-
info("NetData started on pid %d", getpid());
delaycompress
notifempty
sharedscripts
- #
- # if you add netdata to your init.d/system.d
- # comment su & copytruncate and uncomment postrotate
- # to have netdata restart when logs are rotated
- create 0640 netdata netdata
- copytruncate
- #
- #postrotate
- # /sbin/service netdata try-restart >/dev/null
- #endscript
+ create 0664 netdata netdata
+ postrotate
+ /sbin/killall -HUP netdata
+ endscript
}
[Unit]
-Description=Linux real time system monitoring, done right
+Description=Real time performance monitoring
After=network.target httpd.service squid.service nfs-server.service mysqld.service named.service postfix.service
[Service]
-Type=forking
+Type=simple
WorkingDirectory=/tmp
User=netdata
Group=netdata
RuntimeDirectory=netdata
-PIDFile=@localstatedir_POST@/run/netdata/netdata.pid
-ExecStart=@sbindir_POST@/netdata -P @localstatedir_POST@/run/netdata/netdata.pid
+ExecStartPre=/bin/mkdir -p @localstatedir_POST@/run/netdata
+ExecStartPre=/bin/chown -R netdata:netdata @localstatedir_POST@/run/netdata
+ExecStartPre=/bin/chmod 0775 @localstatedir_POST@/run/netdata
+ExecStart=@sbindir_POST@/netdata -D -P @localstatedir_POST@/run/netdata/netdata.pid
+
+# -----------------------------------------------------------------------------
+# Stopping netdata
+
KillMode=mixed
KillSignal=SIGTERM
-TimeoutStopSec=30
-#Hardening
+# saving a big db on slow disks may need some time
+TimeoutStopSec=60
+
+# and disable SIGKILL - if sent during save, we will loose the db
+SendSIGKILL=no
+
+# -----------------------------------------------------------------------------
+# Hardening netdata
+
AmbientCapabilities=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
PrivateTmp=true
ProtectSystem=full
ProtectHome=read-only
-#NoNewPrivileges=true is implicitly set by the MemoryDenyWriteExecute=true
-MemoryDenyWriteExecute=true
+
+# is implicitly set by the MemoryDenyWriteExecute=true
+# NoNewPrivileges=true
+
+# Do not enable - it makes node.js plugins to crash
+# MemoryDenyWriteExecute=true
[Install]
WantedBy=multi-user.target