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