]> arthur.barton.de Git - netdata.git/blob - src/daemon.c
4cf89ec397f244bfc7cb435b27afad21e31e8cd8
[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 <pthread.h>
14 #include <sys/wait.h>
15 #include <sys/stat.h>
16 #include <execinfo.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 void sig_handler(int signo)
29 {
30         switch(signo) {
31                 case SIGILL:
32                 case SIGABRT:
33                 case SIGFPE:
34                 case SIGSEGV:
35                 case SIGBUS:
36                 case SIGSYS:
37                 case SIGTRAP:
38                 case SIGXCPU:
39                 case SIGXFSZ:
40                         infoerr("Death signaled exit (signal %d).", signo);
41                         signal(signo, SIG_DFL);
42                         break;
43
44                 case SIGKILL:
45                 case SIGTERM:
46                 case SIGQUIT:
47                 case SIGINT:
48                 case SIGHUP:
49                 case SIGUSR1:
50                 case SIGUSR2:
51                         infoerr("Signaled exit (signal %d).", signo);
52                         signal(SIGPIPE, SIG_IGN);
53                         signal(SIGTERM, SIG_IGN);
54                         signal(SIGQUIT, SIG_IGN);
55                         signal(SIGHUP,  SIG_IGN);
56                         signal(SIGINT,  SIG_IGN);
57                         signal(SIGCHLD, SIG_IGN);
58                         netdata_cleanup_and_exit(1);
59                         break;
60
61                 case SIGPIPE:
62                         infoerr("Signaled PIPE (signal %d).", signo);
63                         // this is received when web clients send a reset
64                         // no need to log it.
65                         // infoerr("Ignoring signal %d.", signo);
66                         break;
67
68                 default:
69                         info("Signal %d received. Falling back to default action for it.", signo);
70                         signal(signo, SIG_DFL);
71                         break;
72         }
73 }
74
75 char rundir[FILENAME_MAX + 1] = "/var/run/netdata";
76 char pidfile[FILENAME_MAX + 1] = "";
77 void prepare_rundir() {
78         if(getuid() != 0) {
79                 mkdir("/run/user", 0775);
80                 snprintf(rundir, FILENAME_MAX, "/run/user/%d", getuid());
81                 mkdir(rundir, 0775);
82                 snprintf(rundir, FILENAME_MAX, "/run/user/%d/netdata", getuid());
83         }
84
85         snprintf(pidfile, FILENAME_MAX, "%s/netdata.pid", rundir);
86
87         if(mkdir(rundir, 0775) != 0) {
88                 if(errno != EEXIST) error("Cannot create directory '%s'.", rundir);
89         }
90 }
91
92 int become_user(const char *username)
93 {
94         struct passwd *pw = getpwnam(username);
95         if(!pw) {
96                 error("User %s is not present.", username);
97                 return -1;
98         }
99
100         if(chown(rundir, pw->pw_uid, pw->pw_gid) != 0) {
101                 error("Cannot chown directory '%s' to user %s.", rundir, username);
102                 return -1;
103         }
104
105         if(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
106                 error("Cannot switch to user's %s group (gid: %d).", username, pw->pw_gid);
107                 return -1;
108         }
109
110         if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
111                 error("Cannot switch to user %s (uid: %d).", username, pw->pw_uid);
112                 return -1;
113         }
114
115         if(setgid(pw->pw_gid) != 0) {
116                 error("Cannot switch to user's %s group (gid: %d).", username, pw->pw_gid);
117                 return -1;
118         }
119         if(setegid(pw->pw_gid) != 0) {
120                 error("Cannot effectively switch to user's %s group (gid: %d).", username, pw->pw_gid);
121                 return -1;
122         }
123         if(setuid(pw->pw_uid) != 0) {
124                 error("Cannot switch to user %s (uid: %d).", username, pw->pw_uid);
125                 return -1;
126         }
127         if(seteuid(pw->pw_uid) != 0) {
128                 error("Cannot effectively switch to user %s (uid: %d).", username, pw->pw_uid);
129                 return -1;
130         }
131
132         return(0);
133 }
134
135 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)
136 {
137         fflush(NULL);
138
139         // open the files before forking
140         int input_fd = -1, output_fd = -1, error_fd = -1, dev_null = -1;
141
142         if(input && *input) {
143                 if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
144                         error("Cannot open input file '%s'.", input);
145                         return -1;
146                 }
147         }
148
149         if(output && *output) {
150                 if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
151                         error("Cannot open output log file '%s'", output);
152                         if(input_fd != -1) close(input_fd);
153                         return -1;
154                 }
155         }
156
157         if(error && *error) {
158                 if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
159                         error("Cannot open error log file '%s'.", error);
160                         if(input_fd != -1) close(input_fd);
161                         if(output_fd != -1) close(output_fd);
162                         return -1;
163                 }
164         }
165
166         if(access && *access && access_fd) {
167                 if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
168                         error("Cannot open access log file '%s'", access);
169                         if(input_fd != -1) close(input_fd);
170                         if(output_fd != -1) close(output_fd);
171                         if(error_fd != -1) close(error_fd);
172                         return -1;
173                 }
174
175                 if(access_fp) {
176                         *access_fp = fdopen(*access_fd, "w");
177                         if(!*access_fp) {
178                                 error("Cannot migrate file's '%s' fd %d.", access, *access_fd);
179                                 if(input_fd != -1) close(input_fd);
180                                 if(output_fd != -1) close(output_fd);
181                                 if(error_fd != -1) close(error_fd);
182                                 close(*access_fd);
183                                 *access_fd = -1;
184                                 return -1;
185                         }
186                 }
187         }
188
189         if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) {
190                 perror("Cannot open /dev/null");
191                 if(input_fd != -1) close(input_fd);
192                 if(output_fd != -1) close(output_fd);
193                 if(error_fd != -1) close(error_fd);
194                 if(access && access_fd && *access_fd != -1) {
195                         close(*access_fd);
196                         *access_fd = -1;
197                         if(access_fp) {
198                                 fclose(*access_fp);
199                                 *access_fp = NULL;
200                         }
201                 }
202                 return -1;
203         }
204
205         // all files opened
206         // lets do it
207
208         if(!dont_fork) {
209                 int i = fork();
210                 if(i == -1) {
211                         perror("cannot fork");
212                         exit(1);
213                 }
214                 if(i != 0) {
215                         exit(0); // the parent
216                 }
217
218                 // become session leader
219                 if (setsid() < 0) {
220                         perror("Cannot become session leader.");
221                         exit(2);
222                 }
223         }
224
225         signal(SIGCHLD,  SIG_IGN);
226         signal(SIGHUP,   SIG_IGN);
227         signal(SIGWINCH, SIG_IGN);
228
229         // fork() again
230         if(!dont_fork) {
231                 int i = fork();
232                 if(i == -1) {
233                         perror("cannot fork");
234                         exit(1);
235                 }
236                 if(i != 0) {
237                         exit(0); // the parent
238                 }
239         }
240
241         // Set new file permissions
242         umask(0);
243
244         // close all files
245         if(close_all_files) {
246                 int i;
247                 for(i = sysconf(_SC_OPEN_MAX) - 1; i > 0; i--)
248                         if(
249                                 ((access_fd && i != *access_fd) || !access_fd)
250                                 && i != dev_null
251                                 && i != input_fd
252                                 && i != output_fd
253                                 && i != error_fd
254                                 && fd_is_valid(i)
255                                 ) close(i);
256         }
257         else {
258                 close(STDIN_FILENO);
259                 close(STDOUT_FILENO);
260                 close(STDERR_FILENO);
261         }
262
263         // put the opened files
264         // to our standard file descriptors
265         if(input_fd != -1) {
266                 if(input_fd != STDIN_FILENO) {
267                         dup2(input_fd, STDIN_FILENO);
268                         close(input_fd);
269                 }
270                 input_fd = -1;
271         }
272         else dup2(dev_null, STDIN_FILENO);
273
274         if(output_fd != -1) {
275                 if(output_fd != STDOUT_FILENO) {
276                         dup2(output_fd, STDOUT_FILENO);
277                         close(output_fd);
278                 }
279                 output_fd = -1;
280         }
281         else dup2(dev_null, STDOUT_FILENO);
282
283         if(error_fd != -1) {
284                 if(error_fd != STDERR_FILENO) {
285                         dup2(error_fd, STDERR_FILENO);
286                         close(error_fd);
287                 }
288                 error_fd = -1;
289         }
290         else dup2(dev_null, STDERR_FILENO);
291
292         // close /dev/null
293         if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
294                 close(dev_null);
295
296         // generate our pid file
297         {
298                 unlink(pidfile);
299                 int fd = open(pidfile, O_RDWR | O_CREAT, 0666);
300                 if(fd >= 0) {
301                         char b[100];
302                         sprintf(b, "%d\n", getpid());
303                         int i = write(fd, b, strlen(b));
304                         if(i <= 0) perror("Cannot write pid to file.");
305                         close(fd);
306                 }
307         }
308
309         if(user && *user) {
310                 if(become_user(user) != 0) {
311                         error("Cannot become user '%s'. Continuing as we are.", user);
312                 }
313                 else info("Successfully became user '%s'.", user);
314         }
315
316         return(0);
317 }