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