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