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