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