]> arthur.barton.de Git - netdata.git/blob - src/daemon.c
dns_query_time plugin: replace "." with "_" in dimensions
[netdata.git] / src / daemon.c
1 #include "common.h"
2 #include <sched.h>
3
4 char pidfile[FILENAME_MAX + 1] = "";
5
6 void sig_handler_exit(int signo)
7 {
8     if(signo) {
9         error_log_limit_unlimited();
10         error("Received signal %d. Exiting...", signo);
11         netdata_exit = 1;
12     }
13 }
14
15 void sig_handler_logrotate(int signo)
16 {
17     if(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();
22     }
23 }
24
25 void sig_handler_save(int signo)
26 {
27     if(signo) {
28         error_log_limit_unlimited();
29         info("Received signal %d to save the database...", signo);
30         rrdhost_save_all();
31         error_log_limit_reset();
32     }
33 }
34
35 void sig_handler_reload_health(int signo)
36 {
37     if(signo) {
38         error_log_limit_unlimited();
39         info("Received signal %d to reload health configuration...", signo);
40         health_reload();
41         error_log_limit_reset();
42     }
43 }
44
45 static void chown_open_file(int fd, uid_t uid, gid_t gid) {
46     if(fd == -1) return;
47
48     struct stat buf;
49
50     if(fstat(fd, &buf) == -1) {
51         error("Cannot fstat() fd %d", fd);
52         return;
53     }
54
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);
58     }
59 }
60
61 void create_needed_dir(const char *dir, uid_t uid, gid_t gid)
62 {
63     // attempt to create the directory
64     if(mkdir(dir, 0755) == 0) {
65         // we created it
66
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);
70     }
71     else if(errno != EEXIST)
72         // log an error only if the directory does not exist
73         error("Cannot create directory '%s'", dir);
74 }
75
76 int become_user(const char *username, int pid_fd)
77 {
78     struct passwd *pw = getpwnam(username);
79     if(!pw) {
80         error("User %s is not present.", username);
81         return -1;
82     }
83
84     uid_t uid = pw->pw_uid;
85     gid_t gid = pw->pw_gid;
86
87     create_needed_dir(netdata_configured_cache_dir, uid, gid);
88     create_needed_dir(netdata_configured_varlib_dir, uid, gid);
89
90     if(pidfile[0]) {
91         if(chown(pidfile, uid, gid) == -1)
92             error("Cannot chown '%s' to %u:%u", pidfile, (unsigned int)uid, (unsigned int)gid);
93     }
94
95     int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
96     gid_t *supplementary_groups = NULL;
97     if(ngroups) {
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;
103             ngroups = 0;
104         }
105     }
106
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);
111
112     if(supplementary_groups && ngroups) {
113         if(setgroups(ngroups, supplementary_groups) == -1)
114             error("Cannot set supplementary groups for user '%s'", username);
115
116         freez(supplementary_groups);
117         ngroups = 0;
118     }
119
120 #ifdef __APPLE__
121     if(setregid(gid, gid) != 0) {
122 #else
123     if(setresgid(gid, gid, gid) != 0) {
124 #endif /* __APPLE__ */
125         error("Cannot switch to user's %s group (gid: %u).", username, gid);
126         return -1;
127     }
128
129 #ifdef __APPLE__
130     if(setreuid(uid, uid) != 0) {
131 #else
132     if(setresuid(uid, uid, uid) != 0) {
133 #endif /* __APPLE__ */
134         error("Cannot switch to user %s (uid: %u).", username, uid);
135         return -1;
136     }
137
138     if(setgid(gid) != 0) {
139         error("Cannot switch to user's %s group (gid: %u).", username, gid);
140         return -1;
141     }
142     if(setegid(gid) != 0) {
143         error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
144         return -1;
145     }
146     if(setuid(uid) != 0) {
147         error("Cannot switch to user %s (uid: %u).", username, uid);
148         return -1;
149     }
150     if(seteuid(uid) != 0) {
151         error("Cannot effectively switch to user %s (uid: %u).", username, uid);
152         return -1;
153     }
154
155     return(0);
156 }
157
158 static void oom_score_adj(void) {
159     int score = (int)config_get_number(CONFIG_SECTION_GLOBAL, "OOM score", 1000);
160
161     int done = 0;
162     int fd = open("/proc/self/oom_score_adj", O_WRONLY);
163     if(fd != -1) {
164         char buf[10 + 1];
165         ssize_t len = snprintfz(buf, 10, "%d", score);
166         if(len > 0 && write(fd, buf, (size_t)len) == len) done = 1;
167         close(fd);
168     }
169
170     if(!done)
171         error("Cannot adjust my Out-Of-Memory score to %d.", score);
172     else
173         debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score);
174 }
175
176 static void process_nice_level(void) {
177 #ifdef HAVE_NICE
178     int nice_level = (int)config_get_number(CONFIG_SECTION_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);
181 #endif // HAVE_NICE
182 };
183
184 #ifdef HAVE_SCHED_SETSCHEDULER
185
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
190
191 struct sched_def {
192     char *name;
193     int policy;
194     int priority;
195     uint8_t flags;
196 } scheduler_defaults[] = {
197
198         // the order of array members is important!
199         // the first defined is the default used by netdata
200
201         // the available members are important too!
202         // these are all the possible scheduling policies supported by netdata
203
204 #ifdef SCHED_IDLE
205         { "idle", SCHED_IDLE, 0, SCHED_FLAG_NONE },
206 #endif
207
208 #ifdef SCHED_OTHER
209         { "nice",  SCHED_OTHER, 0, SCHED_FLAG_USE_NICE },
210         { "other", SCHED_OTHER, 0, SCHED_FLAG_USE_NICE },
211 #endif
212
213 #ifdef SCHED_RR
214         { "rr", SCHED_RR, 0, SCHED_FLAG_PRIORITY_CONFIGURABLE },
215 #endif
216
217 #ifdef SCHED_FIFO
218         { "fifo", SCHED_FIFO, 0, SCHED_FLAG_PRIORITY_CONFIGURABLE },
219 #endif
220
221 #ifdef SCHED_BATCH
222         { "batch", SCHED_BATCH, 0, SCHED_FLAG_USE_NICE },
223 #endif
224
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 },
228
229         // array termination
230         { NULL, 0, 0, 0 }
231 };
232
233 static void sched_setscheduler_set(void) {
234
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;
239         int found = 0;
240
241         // read the configuration
242         name = config_get(CONFIG_SECTION_GLOBAL, "process scheduling policy", name);
243         int i;
244         for(i = 0 ; scheduler_defaults[i].name ; i++) {
245             if(!strcmp(name, scheduler_defaults[i].name)) {
246                 found = 1;
247                 priority = scheduler_defaults[i].priority;
248                 flags = scheduler_defaults[i].flags;
249
250                 if(flags & SCHED_FLAG_KEEP_AS_IS)
251                     return;
252
253                 if(flags & SCHED_FLAG_PRIORITY_CONFIGURABLE)
254                     priority = (int)config_get_number(CONFIG_SECTION_GLOBAL, "process scheduling priority", priority);
255
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);
260                 }
261 #endif
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);
266                 }
267 #endif
268                 break;
269             }
270         }
271
272         if(!found) {
273             error("Unknown scheduling policy %s - falling back to nice()", name);
274             goto fallback;
275         }
276
277         const struct sched_param param = {
278                 .sched_priority = priority
279         };
280
281         i = sched_setscheduler(0, policy, &param);
282         if(i != 0) {
283             error("Cannot adjust netdata scheduling policy to %s (%d), with priority %d. Falling back to nice", name, policy, priority);
284         }
285         else {
286             debug(D_SYSTEM, "Adjusted netdata scheduling policy to %s (%d), with priority %d.", name, policy, priority);
287             if(!(flags & SCHED_FLAG_USE_NICE))
288                 return;
289         }
290     }
291
292 fallback:
293     process_nice_level();
294 }
295 #else
296 static void sched_setscheduler_set(void) {
297     process_nice_level();
298 }
299 #endif
300
301 int become_daemon(int dont_fork, const char *user)
302 {
303     if(!dont_fork) {
304         int i = fork();
305         if(i == -1) {
306             perror("cannot fork");
307             exit(1);
308         }
309         if(i != 0) {
310             exit(0); // the parent
311         }
312
313         // become session leader
314         if (setsid() < 0) {
315             perror("Cannot become session leader.");
316             exit(2);
317         }
318
319         // fork() again
320         i = fork();
321         if(i == -1) {
322             perror("cannot fork");
323             exit(1);
324         }
325         if(i != 0) {
326             exit(0); // the parent
327         }
328     }
329
330     // generate our pid file
331     int pidfd = -1;
332     if(pidfile[0]) {
333         pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
334         if(pidfd >= 0) {
335             if(ftruncate(pidfd, 0) != 0)
336                 error("Cannot truncate pidfile '%s'.", pidfile);
337
338             char b[100];
339             sprintf(b, "%d\n", getpid());
340             ssize_t i = write(pidfd, b, strlen(b));
341             if(i <= 0)
342                 error("Cannot write pidfile '%s'.", pidfile);
343         }
344         else error("Failed to open pidfile '%s'.", pidfile);
345     }
346
347     // Set new file permissions
348     umask(0007);
349
350     // adjust my Out-Of-Memory score
351     oom_score_adj();
352
353     // never become a problem
354     sched_setscheduler_set();
355
356     if(user && *user) {
357         if(become_user(user, pidfd) != 0) {
358             error("Cannot become user '%s'. Continuing as we are.", user);
359         }
360         else debug(D_SYSTEM, "Successfully became user '%s'.", user);
361     }
362     else {
363         create_needed_dir(netdata_configured_cache_dir, getuid(), getgid());
364         create_needed_dir(netdata_configured_varlib_dir, getuid(), getgid());
365     }
366
367     if(pidfd != -1)
368         close(pidfd);
369
370     return(0);
371 }