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