]> arthur.barton.de Git - netdata.git/blob - src/daemon.c
Merge pull request #132 from mcnewton/debianpkg
[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         uid_t uid = pw->pw_uid;
86         gid_t gid = pw->pw_gid;
87
88         if(pidfile[0] && getuid() != uid) {
89                 // we are dropping privileges
90                 if(chown(pidfile, uid, gid) != 0)
91                         error("Cannot chown pidfile '%s' to user '%s'", pidfile, username);
92
93                 else if(pidfd != -1) {
94                         // not need to keep it open
95                         close(pidfd);
96                         pidfd = -1;
97                 }
98         }
99         else if(pidfd != -1) {
100                 // not need to keep it open
101                 close(pidfd);
102                 pidfd = -1;
103         }
104
105         if(setresgid(gid, gid, gid) != 0) {
106                 error("Cannot switch to user's %s group (gid: %d).", username, gid);
107                 return -1;
108         }
109
110         if(setresuid(uid, uid, uid) != 0) {
111                 error("Cannot switch to user %s (uid: %d).", username, uid);
112                 return -1;
113         }
114
115         if(setgid(gid) != 0) {
116                 error("Cannot switch to user's %s group (gid: %d).", username, gid);
117                 return -1;
118         }
119         if(setegid(gid) != 0) {
120                 error("Cannot effectively switch to user's %s group (gid: %d).", username, gid);
121                 return -1;
122         }
123         if(setuid(uid) != 0) {
124                 error("Cannot switch to user %s (uid: %d).", username, uid);
125                 return -1;
126         }
127         if(seteuid(uid) != 0) {
128                 error("Cannot effectively switch to user %s (uid: %d).", username, 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;
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 = (int) (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         if(pidfile[0]) {
298                 pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
299                 if(pidfd >= 0) {
300                         if(ftruncate(pidfd, 0) != 0)
301                                 error("Cannot truncate pidfile '%s'.", pidfile);
302
303                         char b[100];
304                         sprintf(b, "%d\n", getpid());
305                         ssize_t i = write(pidfd, b, strlen(b));
306                         if(i <= 0)
307                                 error("Cannot write pidfile '%s'.", pidfile);
308
309                         // don't close it, we might need it at exit
310                         // close(pidfd);
311                 }
312                 else error("Failed to open pidfile '%s'.", pidfile);
313         }
314
315         if(user && *user) {
316                 if(become_user(user) != 0) {
317                         error("Cannot become user '%s'. Continuing as we are.", user);
318                 }
319                 else info("Successfully became user '%s'.", user);
320         }
321         else if(pidfd != -1)
322                 close(pidfd);
323
324         return(0);
325 }