]> arthur.barton.de Git - netdata.git/blob - src/daemon.c
chown /var/lib/netdata and /var/cache/netdata only if we created them; chown the...
[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     // attempt to create the directory
64     if(mkdir(dir, 0755) == 0) {
65         // we created it
66
67         // chown it to match the required user
68         if(chown(dir, uid, gid) == -1)
69             error("Cannot chown directory '%s' to %u:%u", dir, (unsigned int)uid, (unsigned int)gid);
70     }
71     else if(errno != EEXIST)
72         // log an error only if the directory does not exist
73         error("Cannot create directory '%s'", dir);
74 }
75
76 int become_user(const char *username, int pid_fd)
77 {
78     struct passwd *pw = getpwnam(username);
79     if(!pw) {
80         error("User %s is not present.", username);
81         return -1;
82     }
83
84     uid_t uid = pw->pw_uid;
85     gid_t gid = pw->pw_gid;
86
87     create_needed_dir(CACHE_DIR, uid, gid);
88     create_needed_dir(VARLIB_DIR, uid, gid);
89
90     if(pidfile[0]) {
91         if(chown(pidfile, uid, gid) == -1)
92             error("Cannot chown '%s' to %u:%u", pidfile, (unsigned int)uid, (unsigned int)gid);
93     }
94
95     int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
96     gid_t *supplementary_groups = NULL;
97     if(ngroups) {
98         supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
99         if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
100             error("Cannot get supplementary groups of user '%s'.", username);
101             freez(supplementary_groups);
102             supplementary_groups = NULL;
103             ngroups = 0;
104         }
105     }
106
107     chown_open_file(STDOUT_FILENO, uid, gid);
108     chown_open_file(STDERR_FILENO, uid, gid);
109     chown_open_file(stdaccess_fd, uid, gid);
110     chown_open_file(pid_fd, uid, gid);
111
112     if(supplementary_groups && ngroups) {
113         if(setgroups(ngroups, supplementary_groups) == -1)
114             error("Cannot set supplementary groups for user '%s'", username);
115
116         freez(supplementary_groups);
117         supplementary_groups = NULL;
118         ngroups = 0;
119     }
120
121     if(setresgid(gid, gid, gid) != 0) {
122         error("Cannot switch to user's %s group (gid: %u).", username, gid);
123         return -1;
124     }
125
126     if(setresuid(uid, uid, uid) != 0) {
127         error("Cannot switch to user %s (uid: %u).", username, uid);
128         return -1;
129     }
130
131     if(setgid(gid) != 0) {
132         error("Cannot switch to user's %s group (gid: %u).", username, gid);
133         return -1;
134     }
135     if(setegid(gid) != 0) {
136         error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
137         return -1;
138     }
139     if(setuid(uid) != 0) {
140         error("Cannot switch to user %s (uid: %u).", username, uid);
141         return -1;
142     }
143     if(seteuid(uid) != 0) {
144         error("Cannot effectively switch to user %s (uid: %u).", username, uid);
145         return -1;
146     }
147
148     return(0);
149 }
150
151 void oom_score_adj(int score) {
152     int done = 0;
153     int fd = open("/proc/self/oom_score_adj", O_WRONLY);
154     if(fd != -1) {
155         char buf[10 + 1];
156         ssize_t len = snprintfz(buf, 10, "%d", score);
157         if(write(fd, buf, len) == len) done = 1;
158         close(fd);
159     }
160
161     if(!done)
162         error("Cannot adjust my Out-Of-Memory score to %d.", score);
163     else
164         debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score);
165 }
166
167 int sched_setscheduler_idle(void) {
168 #ifdef SCHED_IDLE
169     const struct sched_param param = {
170         .sched_priority = 0
171     };
172
173     int i = sched_setscheduler(0, SCHED_IDLE, &param);
174     if(i != 0)
175         error("Cannot adjust my scheduling priority to IDLE.");
176     else
177         debug(D_SYSTEM, "Adjusted my scheduling priority to IDLE.");
178
179     return i;
180 #else
181     return -1;
182 #endif
183 }
184
185 int become_daemon(int dont_fork, const char *user)
186 {
187     if(!dont_fork) {
188         int i = fork();
189         if(i == -1) {
190             perror("cannot fork");
191             exit(1);
192         }
193         if(i != 0) {
194             exit(0); // the parent
195         }
196
197         // become session leader
198         if (setsid() < 0) {
199             perror("Cannot become session leader.");
200             exit(2);
201         }
202
203         // fork() again
204         i = fork();
205         if(i == -1) {
206             perror("cannot fork");
207             exit(1);
208         }
209         if(i != 0) {
210             exit(0); // the parent
211         }
212     }
213
214     // generate our pid file
215     int pidfd = -1;
216     if(pidfile[0]) {
217         pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
218         if(pidfd >= 0) {
219             if(ftruncate(pidfd, 0) != 0)
220                 error("Cannot truncate pidfile '%s'.", pidfile);
221
222             char b[100];
223             sprintf(b, "%d\n", getpid());
224             ssize_t i = write(pidfd, b, strlen(b));
225             if(i <= 0)
226                 error("Cannot write pidfile '%s'.", pidfile);
227         }
228         else error("Failed to open pidfile '%s'.", pidfile);
229     }
230
231     // Set new file permissions
232     umask(0002);
233
234     // adjust my Out-Of-Memory score
235     oom_score_adj(1000);
236
237     // never become a problem
238     if(sched_setscheduler_idle() != 0) {
239         if(nice(19) == -1) error("Cannot lower my CPU priority.");
240         else debug(D_SYSTEM, "Set my nice value to 19.");
241     }
242
243     if(user && *user) {
244         if(become_user(user, pidfd) != 0) {
245             error("Cannot become user '%s'. Continuing as we are.", user);
246         }
247         else debug(D_SYSTEM, "Successfully became user '%s'.", user);
248     }
249     else {
250         create_needed_dir(CACHE_DIR, getuid(), getgid());
251         create_needed_dir(VARLIB_DIR, getuid(), getgid());
252     }
253
254     if(pidfd != -1) {
255         close(pidfd);
256         pidfd = -1;
257     }
258
259     return(0);
260 }