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