4 char pidfile[FILENAME_MAX + 1] = "";
6 void sig_handler_exit(int signo)
9 error_log_limit_unlimited();
10 error("Received signal %d. Exiting...", signo);
15 void sig_handler_logrotate(int signo)
18 error_log_limit_unlimited();
19 info("Received signal %d to re-open the log files", signo);
20 reopen_all_log_files();
21 error_log_limit_reset();
25 void sig_handler_save(int signo)
28 error_log_limit_unlimited();
29 info("Received signal %d to save the database...", signo);
31 error_log_limit_reset();
35 void sig_handler_reload_health(int signo)
38 error_log_limit_unlimited();
39 info("Received signal %d to reload health configuration...", signo);
41 error_log_limit_reset();
45 static void chown_open_file(int fd, uid_t uid, gid_t gid) {
50 if(fstat(fd, &buf) == -1) {
51 error("Cannot fstat() fd %d", fd);
55 if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
56 if(fchown(fd, uid, gid) == -1)
57 error("Cannot fchown() fd %d.", fd);
61 void create_needed_dir(const char *dir, uid_t uid, gid_t gid)
63 // attempt to create the directory
64 if(mkdir(dir, 0755) == 0) {
67 // chown it to match the required user
68 if(chown(dir, uid, gid) == -1)
69 error("Cannot chown directory '%s' to %u:%u", dir, (unsigned int)uid, (unsigned int)gid);
71 else if(errno != EEXIST)
72 // log an error only if the directory does not exist
73 error("Cannot create directory '%s'", dir);
76 int become_user(const char *username, int pid_fd)
78 struct passwd *pw = getpwnam(username);
80 error("User %s is not present.", username);
84 uid_t uid = pw->pw_uid;
85 gid_t gid = pw->pw_gid;
87 create_needed_dir(CACHE_DIR, uid, gid);
88 create_needed_dir(VARLIB_DIR, uid, gid);
91 if(chown(pidfile, uid, gid) == -1)
92 error("Cannot chown '%s' to %u:%u", pidfile, (unsigned int)uid, (unsigned int)gid);
95 int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
96 gid_t *supplementary_groups = NULL;
98 supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
99 if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
100 error("Cannot get supplementary groups of user '%s'.", username);
101 freez(supplementary_groups);
102 supplementary_groups = NULL;
107 chown_open_file(STDOUT_FILENO, uid, gid);
108 chown_open_file(STDERR_FILENO, uid, gid);
109 chown_open_file(stdaccess_fd, uid, gid);
110 chown_open_file(pid_fd, uid, gid);
112 if(supplementary_groups && ngroups) {
113 if(setgroups(ngroups, supplementary_groups) == -1)
114 error("Cannot set supplementary groups for user '%s'", username);
116 freez(supplementary_groups);
121 if(setregid(gid, gid) != 0) {
123 if(setresgid(gid, gid, gid) != 0) {
124 #endif /* __APPLE__ */
125 error("Cannot switch to user's %s group (gid: %u).", username, gid);
130 if(setreuid(uid, uid) != 0) {
132 if(setresuid(uid, uid, uid) != 0) {
133 #endif /* __APPLE__ */
134 error("Cannot switch to user %s (uid: %u).", username, uid);
138 if(setgid(gid) != 0) {
139 error("Cannot switch to user's %s group (gid: %u).", username, gid);
142 if(setegid(gid) != 0) {
143 error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
146 if(setuid(uid) != 0) {
147 error("Cannot switch to user %s (uid: %u).", username, uid);
150 if(seteuid(uid) != 0) {
151 error("Cannot effectively switch to user %s (uid: %u).", username, uid);
158 static void oom_score_adj(void) {
159 int score = (int)config_get_number("global", "OOM score", 1000);
162 int fd = open("/proc/self/oom_score_adj", O_WRONLY);
165 ssize_t len = snprintfz(buf, 10, "%d", score);
166 if(len > 0 && write(fd, buf, (size_t)len) == len) done = 1;
171 error("Cannot adjust my Out-Of-Memory score to %d.", score);
173 debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score);
176 static void process_nice_level(void) {
178 int nice_level = (int)config_get_number("global", "process nice level", 19);
179 if(nice(nice_level) == -1) error("Cannot set netdata CPU nice level to %d.", nice_level);
180 else debug(D_SYSTEM, "Set netdata nice level to %d.", nice_level);
184 #ifdef HAVE_SCHED_SETSCHEDULER
186 #define SCHED_FLAG_NONE 0x00
187 #define SCHED_FLAG_PRIORITY_CONFIGURABLE 0x01 // the priority is user configurable
188 #define SCHED_FLAG_KEEP_AS_IS 0x04 // do not attempt to set policy, priority or nice()
189 #define SCHED_FLAG_USE_NICE 0x08 // use nice() after setting this policy
196 } scheduler_defaults[] = {
198 // the order of array members is important!
199 // the first defined is the default used by netdata
201 // the available members are important too!
202 // these are all the possible scheduling policies supported by netdata
205 { "idle", SCHED_IDLE, 0, SCHED_FLAG_NONE },
209 { "nice", SCHED_OTHER, 0, SCHED_FLAG_USE_NICE },
210 { "other", SCHED_OTHER, 0, SCHED_FLAG_USE_NICE },
214 { "rr", SCHED_RR, 0, SCHED_FLAG_PRIORITY_CONFIGURABLE },
218 { "fifo", SCHED_FIFO, 0, SCHED_FLAG_PRIORITY_CONFIGURABLE },
222 { "batch", SCHED_BATCH, 0, SCHED_FLAG_USE_NICE },
225 // do not change the scheduling priority
226 { "keep", 0, 0, SCHED_FLAG_KEEP_AS_IS },
227 { "none", 0, 0, SCHED_FLAG_KEEP_AS_IS },
233 static void sched_setscheduler_set(void) {
235 if(scheduler_defaults[0].name) {
236 const char *name = scheduler_defaults[0].name;
237 int policy = scheduler_defaults[0].policy, priority = scheduler_defaults[0].priority;
238 uint8_t flags = scheduler_defaults[0].flags;
241 // read the configuration
242 name = config_get("global", "process scheduling policy", name);
244 for(i = 0 ; scheduler_defaults[i].name ; i++) {
245 if(!strcmp(name, scheduler_defaults[i].name)) {
247 priority = scheduler_defaults[i].priority;
248 flags = scheduler_defaults[i].flags;
250 if(flags & SCHED_FLAG_KEEP_AS_IS)
253 if(flags & SCHED_FLAG_PRIORITY_CONFIGURABLE)
254 priority = (int)config_get_number("global", "process scheduling priority", priority);
256 #ifdef HAVE_SCHED_GET_PRIORITY_MIN
257 if(priority < sched_get_priority_min(policy)) {
258 error("scheduler %s priority %d is below the minimum %d. Using the minimum.", name, priority, sched_get_priority_min(policy));
259 priority = sched_get_priority_min(policy);
262 #ifdef HAVE_SCHED_GET_PRIORITY_MAX
263 if(priority > sched_get_priority_max(policy)) {
264 error("scheduler %s priority %d is above the maximum %d. Using the maximum.", name, priority, sched_get_priority_max(policy));
265 priority = sched_get_priority_max(policy);
273 error("Unknown scheduling policy %s - falling back to nice()", name);
277 const struct sched_param param = {
278 .sched_priority = priority
281 i = sched_setscheduler(0, policy, ¶m);
283 error("Cannot adjust netdata scheduling policy to %s (%d), with priority %d. Falling back to nice", name, policy, priority);
286 debug(D_SYSTEM, "Adjusted netdata scheduling policy to %s (%d), with priority %d.", name, policy, priority);
287 if(!(flags & SCHED_FLAG_USE_NICE))
293 process_nice_level();
296 static void sched_setscheduler_set(void) {
297 process_nice_level();
301 int become_daemon(int dont_fork, const char *user)
306 perror("cannot fork");
310 exit(0); // the parent
313 // become session leader
315 perror("Cannot become session leader.");
322 perror("cannot fork");
326 exit(0); // the parent
330 // generate our pid file
333 pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
335 if(ftruncate(pidfd, 0) != 0)
336 error("Cannot truncate pidfile '%s'.", pidfile);
339 sprintf(b, "%d\n", getpid());
340 ssize_t i = write(pidfd, b, strlen(b));
342 error("Cannot write pidfile '%s'.", pidfile);
344 else error("Failed to open pidfile '%s'.", pidfile);
347 // Set new file permissions
350 // adjust my Out-Of-Memory score
353 // never become a problem
354 sched_setscheduler_set();
357 if(become_user(user, pidfd) != 0) {
358 error("Cannot become user '%s'. Continuing as we are.", user);
360 else debug(D_SYSTEM, "Successfully became user '%s'.", user);
363 create_needed_dir(CACHE_DIR, getuid(), getgid());
364 create_needed_dir(VARLIB_DIR, getuid(), getgid());