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