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