]> arthur.barton.de Git - netdata.git/blob - src/daemon.c
netdata now adjusts its scheduling priority to IDLE and its Out-Of-Memory score to...
[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_save(int signo)
16 {
17         if(signo) {
18                 info("Received signal %d to save the database...", signo);
19                 rrdset_save_all();
20         }
21 }
22
23 static void properly_chown_netdata_generated_file(int fd, uid_t uid, gid_t gid) {
24         if(fd == -1) return;
25
26         struct stat buf;
27
28         if(fstat(fd, &buf) == -1) {
29                 error("Cannot fstat() fd %d", fd);
30                 return;
31         }
32
33         if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
34                 if(fchown(fd, uid, gid) == -1)
35                         error("Cannot fchown() fd %d.", fd);
36         }
37 }
38
39 int become_user(const char *username, int access_fd, int output_fd, int error_fd, int pid_fd)
40 {
41         struct passwd *pw = getpwnam(username);
42         if(!pw) {
43                 error("User %s is not present.", username);
44                 return -1;
45         }
46
47         uid_t uid = pw->pw_uid;
48         gid_t gid = pw->pw_gid;
49
50         int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
51         gid_t *supplementary_groups = NULL;
52         if(ngroups) {
53                 supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
54                 if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
55                         error("Cannot get supplementary groups of user '%s'.", username);
56                         freez(supplementary_groups);
57                         supplementary_groups = NULL;
58                         ngroups = 0;
59                 }
60         }
61
62         properly_chown_netdata_generated_file(access_fd, uid, gid);
63         properly_chown_netdata_generated_file(output_fd, uid, gid);
64         properly_chown_netdata_generated_file(error_fd, uid, gid);
65         properly_chown_netdata_generated_file(pid_fd, uid, gid);
66
67         if(supplementary_groups && ngroups) {
68                 if(setgroups(ngroups, supplementary_groups) == -1)
69                         error("Cannot set supplementary groups for user '%s'", username);
70
71                 freez(supplementary_groups);
72                 supplementary_groups = NULL;
73                 ngroups = 0;
74         }
75
76         if(setresgid(gid, gid, gid) != 0) {
77                 error("Cannot switch to user's %s group (gid: %u).", username, gid);
78                 return -1;
79         }
80
81         if(setresuid(uid, uid, uid) != 0) {
82                 error("Cannot switch to user %s (uid: %u).", username, uid);
83                 return -1;
84         }
85
86         if(setgid(gid) != 0) {
87                 error("Cannot switch to user's %s group (gid: %u).", username, gid);
88                 return -1;
89         }
90         if(setegid(gid) != 0) {
91                 error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
92                 return -1;
93         }
94         if(setuid(uid) != 0) {
95                 error("Cannot switch to user %s (uid: %u).", username, uid);
96                 return -1;
97         }
98         if(seteuid(uid) != 0) {
99                 error("Cannot effectively switch to user %s (uid: %u).", username, uid);
100                 return -1;
101         }
102
103         return(0);
104 }
105
106 void oom_score_adj(int score) {
107         int done = 0;
108         int fd = open("/proc/self/oom_score_adj", O_WRONLY);
109         if(fd != -1) {
110                 char buf[10 + 1];
111                 ssize_t len = snprintfz(buf, 10, "%d", score);
112                 if(write(fd, buf, len) == len) done = 1;
113                 close(fd);
114         }
115
116         if(!done)
117                 error("Cannot adjust my Out-Of-Memory score to %d.", score);
118         else
119                 info("Adjusted my Out-Of-Memory score to %d.", score);
120 }
121
122 int sched_setscheduler_idle(void) {
123         const struct sched_param param = {
124                 .sched_priority = 0
125         };
126
127         int i = sched_setscheduler(0, SCHED_IDLE, &param);
128         if(i != 0)
129                 error("Cannot adjust my scheduling priority to IDLE.");
130         else
131                 info("Adjusted my scheduling priority to IDLE.");
132
133         return i;
134 }
135
136 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)
137 {
138         fflush(NULL);
139
140         // open the files before forking
141         int input_fd = -1, output_fd = -1, error_fd = -1, dev_null;
142
143         if(input && *input) {
144                 if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
145                         error("Cannot open input file '%s'.", input);
146                         return -1;
147                 }
148         }
149
150         if(output && *output && strcmp(output, "/dev/null") != 0) {
151                 if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
152                         error("Cannot open output log file '%s'", output);
153                         if(input_fd != -1) close(input_fd);
154                         return -1;
155                 }
156         }
157
158         if(error && *error && strcmp(error, "/dev/null") != 0) {
159                 if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
160                         error("Cannot open error log file '%s'.", error);
161                         if(input_fd != -1) close(input_fd);
162                         if(output_fd != -1) close(output_fd);
163                         return -1;
164                 }
165         }
166
167         if(access && *access && access_fd && strcmp(access, "/dev/null") != 0) {
168                 if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
169                         error("Cannot open access log file '%s'", access);
170                         if(input_fd != -1) close(input_fd);
171                         if(output_fd != -1) close(output_fd);
172                         if(error_fd != -1) close(error_fd);
173                         return -1;
174                 }
175
176                 if(access_fp) {
177                         *access_fp = fdopen(*access_fd, "w");
178                         if(!*access_fp) {
179                                 error("Cannot migrate file's '%s' fd %d.", access, *access_fd);
180                                 if(input_fd != -1) close(input_fd);
181                                 if(output_fd != -1) close(output_fd);
182                                 if(error_fd != -1) close(error_fd);
183                                 close(*access_fd);
184                                 *access_fd = -1;
185                                 return -1;
186                         }
187                         if(setvbuf(*access_fp, NULL, _IOLBF, 0) != 0)
188                                 error("Cannot set line buffering on access.log");
189                 }
190         }
191
192         if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) {
193                 perror("Cannot open /dev/null");
194                 if(input_fd != -1) close(input_fd);
195                 if(output_fd != -1) close(output_fd);
196                 if(error_fd != -1) close(error_fd);
197                 if(access && access_fd && *access_fd != -1) {
198                         close(*access_fd);
199                         *access_fd = -1;
200                         if(access_fp) {
201                                 fclose(*access_fp);
202                                 *access_fp = NULL;
203                         }
204                 }
205                 return -1;
206         }
207
208         // all files opened
209         // lets do it
210
211         if(!dont_fork) {
212                 int i = fork();
213                 if(i == -1) {
214                         perror("cannot fork");
215                         exit(1);
216                 }
217                 if(i != 0) {
218                         exit(0); // the parent
219                 }
220
221                 // become session leader
222                 if (setsid() < 0) {
223                         perror("Cannot become session leader.");
224                         exit(2);
225                 }
226
227                 // fork() again
228                 i = fork();
229                 if(i == -1) {
230                         perror("cannot fork");
231                         exit(1);
232                 }
233                 if(i != 0) {
234                         exit(0); // the parent
235                 }
236         }
237
238         // close all files
239         if(close_all_files) {
240                 int i;
241                 for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
242                         if(
243                                 ((access_fd && i != *access_fd) || !access_fd)
244                                 && i != dev_null
245                                 && i != input_fd
246                                 && i != output_fd
247                                 && i != error_fd
248                                 && fd_is_valid(i)
249                                 ) close(i);
250         }
251         else {
252                 close(STDIN_FILENO);
253                 close(STDOUT_FILENO);
254                 close(STDERR_FILENO);
255         }
256
257         // put the opened files
258         // to our standard file descriptors
259         if(input_fd != -1) {
260                 if(input_fd != STDIN_FILENO) {
261                         dup2(input_fd, STDIN_FILENO);
262                         close(input_fd);
263                 }
264                 input_fd = -1;
265         }
266         else dup2(dev_null, STDIN_FILENO);
267
268         if(output_fd != -1) {
269                 if(output_fd != STDOUT_FILENO) {
270                         dup2(output_fd, STDOUT_FILENO);
271                         close(output_fd);
272                 }
273
274                 if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
275                         error("Cannot set line buffering on debug.log");
276
277                 output_fd = STDOUT_FILENO;
278         }
279         else dup2(dev_null, STDOUT_FILENO);
280
281         if(error_fd != -1) {
282                 if(error_fd != STDERR_FILENO) {
283                         dup2(error_fd, STDERR_FILENO);
284                         close(error_fd);
285                 }
286
287                 if(setvbuf(stderr, NULL, _IOLBF, 0) != 0)
288                         error("Cannot set line buffering on error.log");
289
290                 error_fd = STDERR_FILENO;
291         }
292         else dup2(dev_null, STDERR_FILENO);
293
294         // close /dev/null
295         if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
296                 close(dev_null);
297
298         // generate our pid file
299         int pidfd = -1;
300         if(pidfile[0]) {
301                 pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
302                 if(pidfd >= 0) {
303                         if(ftruncate(pidfd, 0) != 0)
304                                 error("Cannot truncate pidfile '%s'.", pidfile);
305
306                         char b[100];
307                         sprintf(b, "%d\n", getpid());
308                         ssize_t i = write(pidfd, b, strlen(b));
309                         if(i <= 0)
310                                 error("Cannot write pidfile '%s'.", pidfile);
311                 }
312                 else error("Failed to open pidfile '%s'.", pidfile);
313         }
314
315         // Set new file permissions
316         umask(0002);
317
318         // adjust my Out-Of-Memory score
319         oom_score_adj(1000);
320
321         // never become a problem
322         if(sched_setscheduler_idle() != 0) {
323                 if(nice(19) == -1) error("Cannot lower my CPU priority.");
324                 else info("Set my nice value to 19.");
325         }
326
327         if(user && *user) {
328                 if(become_user(user, (access_fd)?*access_fd:-1, output_fd, error_fd, pidfd) != 0) {
329                         error("Cannot become user '%s'. Continuing as we are.", user);
330                 }
331                 else info("Successfully became user '%s'.", user);
332         }
333
334         if(pidfd != -1) {
335                 close(pidfd);
336                 pidfd = -1;
337         }
338
339         return(0);
340 }