]> arthur.barton.de Git - netdata.git/blob - src/daemon.c
create /var/cache/netdata and /var/lib/netdata if they do not exist; fixes #1204
[netdata.git] / src / daemon.c
1 #include "common.h"
2 #include <sched.h>
3
4 char pidfile[FILENAME_MAX + 1] = "";
5
6 void sig_handler_exit(int signo)
7 {
8     if(signo) {
9         error_log_limit_unlimited();
10         error("Received signal %d. Exiting...", signo);
11         netdata_exit = 1;
12     }
13 }
14
15 void sig_handler_logrotate(int signo)
16 {
17     if(signo) {
18         error_log_limit_unlimited();
19         info("Received signal %d to re-open the log files", signo);
20         reopen_all_log_files();
21         error_log_limit_reset();
22     }
23 }
24
25 void sig_handler_save(int signo)
26 {
27     if(signo) {
28         error_log_limit_unlimited();
29         info("Received signal %d to save the database...", signo);
30         rrdset_save_all();
31         error_log_limit_reset();
32     }
33 }
34
35 void sig_handler_reload_health(int signo)
36 {
37     if(signo) {
38         error_log_limit_unlimited();
39         info("Received signal %d to reload health configuration...", signo);
40         health_reload();
41         error_log_limit_reset();
42     }
43 }
44
45 static void chown_open_file(int fd, uid_t uid, gid_t gid) {
46     if(fd == -1) return;
47
48     struct stat buf;
49
50     if(fstat(fd, &buf) == -1) {
51         error("Cannot fstat() fd %d", fd);
52         return;
53     }
54
55     if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
56         if(fchown(fd, uid, gid) == -1)
57             error("Cannot fchown() fd %d.", fd);
58     }
59 }
60
61 void create_needed_dir(const char *dir, uid_t uid, gid_t gid)
62 {
63     if(mkdir(dir, 0755) == -1 && errno != EEXIST)
64         error("Cannot create directory '%s'", dir);
65
66     if(chown(dir, uid, gid) == -1)
67         error("Cannot chown directory '%s' to %u:%u", dir, (unsigned int)uid, (unsigned int)gid);
68 }
69
70 int become_user(const char *username, int pid_fd)
71 {
72     struct passwd *pw = getpwnam(username);
73     if(!pw) {
74         error("User %s is not present.", username);
75         return -1;
76     }
77
78     uid_t uid = pw->pw_uid;
79     gid_t gid = pw->pw_gid;
80
81     create_needed_dir(CACHE_DIR, uid, gid);
82     create_needed_dir(VARLIB_DIR, uid, gid);
83
84     int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
85     gid_t *supplementary_groups = NULL;
86     if(ngroups) {
87         supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
88         if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
89             error("Cannot get supplementary groups of user '%s'.", username);
90             freez(supplementary_groups);
91             supplementary_groups = NULL;
92             ngroups = 0;
93         }
94     }
95
96     chown_open_file(STDOUT_FILENO, uid, gid);
97     chown_open_file(STDERR_FILENO, uid, gid);
98     chown_open_file(stdaccess_fd, uid, gid);
99     chown_open_file(pid_fd, uid, gid);
100
101     if(supplementary_groups && ngroups) {
102         if(setgroups(ngroups, supplementary_groups) == -1)
103             error("Cannot set supplementary groups for user '%s'", username);
104
105         freez(supplementary_groups);
106         supplementary_groups = NULL;
107         ngroups = 0;
108     }
109
110     if(setresgid(gid, gid, gid) != 0) {
111         error("Cannot switch to user's %s group (gid: %u).", username, gid);
112         return -1;
113     }
114
115     if(setresuid(uid, uid, uid) != 0) {
116         error("Cannot switch to user %s (uid: %u).", username, uid);
117         return -1;
118     }
119
120     if(setgid(gid) != 0) {
121         error("Cannot switch to user's %s group (gid: %u).", username, gid);
122         return -1;
123     }
124     if(setegid(gid) != 0) {
125         error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
126         return -1;
127     }
128     if(setuid(uid) != 0) {
129         error("Cannot switch to user %s (uid: %u).", username, uid);
130         return -1;
131     }
132     if(seteuid(uid) != 0) {
133         error("Cannot effectively switch to user %s (uid: %u).", username, uid);
134         return -1;
135     }
136
137     return(0);
138 }
139
140 void oom_score_adj(int score) {
141     int done = 0;
142     int fd = open("/proc/self/oom_score_adj", O_WRONLY);
143     if(fd != -1) {
144         char buf[10 + 1];
145         ssize_t len = snprintfz(buf, 10, "%d", score);
146         if(write(fd, buf, len) == len) done = 1;
147         close(fd);
148     }
149
150     if(!done)
151         error("Cannot adjust my Out-Of-Memory score to %d.", score);
152     else
153         debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score);
154 }
155
156 int sched_setscheduler_idle(void) {
157 #ifdef SCHED_IDLE
158     const struct sched_param param = {
159         .sched_priority = 0
160     };
161
162     int i = sched_setscheduler(0, SCHED_IDLE, &param);
163     if(i != 0)
164         error("Cannot adjust my scheduling priority to IDLE.");
165     else
166         debug(D_SYSTEM, "Adjusted my scheduling priority to IDLE.");
167
168     return i;
169 #else
170     return -1;
171 #endif
172 }
173
174 int become_daemon(int dont_fork, const char *user)
175 {
176     if(!dont_fork) {
177         int i = fork();
178         if(i == -1) {
179             perror("cannot fork");
180             exit(1);
181         }
182         if(i != 0) {
183             exit(0); // the parent
184         }
185
186         // become session leader
187         if (setsid() < 0) {
188             perror("Cannot become session leader.");
189             exit(2);
190         }
191
192         // fork() again
193         i = fork();
194         if(i == -1) {
195             perror("cannot fork");
196             exit(1);
197         }
198         if(i != 0) {
199             exit(0); // the parent
200         }
201     }
202
203     // generate our pid file
204     int pidfd = -1;
205     if(pidfile[0]) {
206         pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
207         if(pidfd >= 0) {
208             if(ftruncate(pidfd, 0) != 0)
209                 error("Cannot truncate pidfile '%s'.", pidfile);
210
211             char b[100];
212             sprintf(b, "%d\n", getpid());
213             ssize_t i = write(pidfd, b, strlen(b));
214             if(i <= 0)
215                 error("Cannot write pidfile '%s'.", pidfile);
216         }
217         else error("Failed to open pidfile '%s'.", pidfile);
218     }
219
220     // Set new file permissions
221     umask(0002);
222
223     // adjust my Out-Of-Memory score
224     oom_score_adj(1000);
225
226     // never become a problem
227     if(sched_setscheduler_idle() != 0) {
228         if(nice(19) == -1) error("Cannot lower my CPU priority.");
229         else debug(D_SYSTEM, "Set my nice value to 19.");
230     }
231
232     if(user && *user) {
233         if(become_user(user, pidfd) != 0) {
234             error("Cannot become user '%s'. Continuing as we are.", user);
235         }
236         else debug(D_SYSTEM, "Successfully became user '%s'.", user);
237     }
238     else {
239         create_needed_dir(CACHE_DIR, getuid(), getgid());
240         create_needed_dir(VARLIB_DIR, getuid(), getgid());
241     }
242
243     if(pidfd != -1) {
244         close(pidfd);
245         pidfd = -1;
246     }
247
248     return(0);
249 }