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