1
2 /* $Id: fpm_unix.c,v 1.25.2.1 2008/12/13 03:18:23 anight Exp $ */
3 /* (c) 2007,2008 Andrei Nigmatulin */
4
5 #include "fpm_config.h"
6
7 #include <string.h>
8 #include <sys/time.h>
9 #include <sys/resource.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <pwd.h>
14 #include <grp.h>
15
16 #ifdef HAVE_PRCTL
17 #include <sys/prctl.h>
18 #endif
19
20 #include "fpm.h"
21 #include "fpm_conf.h"
22 #include "fpm_cleanup.h"
23 #include "fpm_clock.h"
24 #include "fpm_stdio.h"
25 #include "fpm_unix.h"
26 #include "fpm_signals.h"
27 #include "zlog.h"
28
29 size_t fpm_pagesize;
30
fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s * wp)31 int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
32 {
33 struct fpm_worker_pool_config_s *c = wp->config;
34
35 /* uninitialized */
36 wp->socket_uid = -1;
37 wp->socket_gid = -1;
38 wp->socket_mode = 0660;
39
40 if (!c) {
41 return 0;
42 }
43
44 if (c->listen_owner && *c->listen_owner) {
45 struct passwd *pwd;
46
47 pwd = getpwnam(c->listen_owner);
48 if (!pwd) {
49 zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner);
50 return -1;
51 }
52
53 wp->socket_uid = pwd->pw_uid;
54 wp->socket_gid = pwd->pw_gid;
55 }
56
57 if (c->listen_group && *c->listen_group) {
58 struct group *grp;
59
60 grp = getgrnam(c->listen_group);
61 if (!grp) {
62 zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group);
63 return -1;
64 }
65 wp->socket_gid = grp->gr_gid;
66 }
67
68 if (c->listen_mode && *c->listen_mode) {
69 wp->socket_mode = strtoul(c->listen_mode, 0, 8);
70 }
71 return 0;
72 }
73 /* }}} */
74
fpm_unix_conf_wp(struct fpm_worker_pool_s * wp)75 static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
76 {
77 struct passwd *pwd;
78 int is_root = !geteuid();
79
80 if (is_root) {
81 if (wp->config->user && *wp->config->user) {
82 if (strlen(wp->config->user) == strspn(wp->config->user, "0123456789")) {
83 wp->set_uid = strtoul(wp->config->user, 0, 10);
84 } else {
85 struct passwd *pwd;
86
87 pwd = getpwnam(wp->config->user);
88 if (!pwd) {
89 zlog(ZLOG_ERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, wp->config->user);
90 return -1;
91 }
92
93 wp->set_uid = pwd->pw_uid;
94 wp->set_gid = pwd->pw_gid;
95
96 wp->user = strdup(pwd->pw_name);
97 wp->home = strdup(pwd->pw_dir);
98 }
99 }
100
101 if (wp->config->group && *wp->config->group) {
102 if (strlen(wp->config->group) == strspn(wp->config->group, "0123456789")) {
103 wp->set_gid = strtoul(wp->config->group, 0, 10);
104 } else {
105 struct group *grp;
106
107 grp = getgrnam(wp->config->group);
108 if (!grp) {
109 zlog(ZLOG_ERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, wp->config->group);
110 return -1;
111 }
112 wp->set_gid = grp->gr_gid;
113 }
114 }
115
116 if (!fpm_globals.run_as_root) {
117 if (wp->set_uid == 0 || wp->set_gid == 0) {
118 zlog(ZLOG_ERROR, "[pool %s] please specify user and group other than root", wp->config->name);
119 return -1;
120 }
121 }
122 } else { /* not root */
123 if (wp->config->user && *wp->config->user) {
124 zlog(ZLOG_NOTICE, "[pool %s] 'user' directive is ignored when FPM is not running as root", wp->config->name);
125 }
126 if (wp->config->group && *wp->config->group) {
127 zlog(ZLOG_NOTICE, "[pool %s] 'group' directive is ignored when FPM is not running as root", wp->config->name);
128 }
129 if (wp->config->chroot && *wp->config->chroot) {
130 zlog(ZLOG_NOTICE, "[pool %s] 'chroot' directive is ignored when FPM is not running as root", wp->config->name);
131 }
132 if (wp->config->process_priority != 64) {
133 zlog(ZLOG_NOTICE, "[pool %s] 'process.priority' directive is ignored when FPM is not running as root", wp->config->name);
134 }
135
136 /* set up HOME and USER anyway */
137 pwd = getpwuid(getuid());
138 if (pwd) {
139 wp->user = strdup(pwd->pw_name);
140 wp->home = strdup(pwd->pw_dir);
141 }
142 }
143 return 0;
144 }
145 /* }}} */
146
fpm_unix_init_child(struct fpm_worker_pool_s * wp)147 int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
148 {
149 int is_root = !geteuid();
150 int made_chroot = 0;
151
152 if (wp->config->rlimit_files) {
153 struct rlimit r;
154
155 r.rlim_max = r.rlim_cur = (rlim_t) wp->config->rlimit_files;
156
157 if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
158 zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_files for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", wp->config->name, wp->config->rlimit_files);
159 }
160 }
161
162 if (wp->config->rlimit_core) {
163 struct rlimit r;
164
165 r.rlim_max = r.rlim_cur = wp->config->rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) wp->config->rlimit_core;
166
167 if (0 > setrlimit(RLIMIT_CORE, &r)) {
168 zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", wp->config->name, wp->config->rlimit_core);
169 }
170 }
171
172 if (is_root && wp->config->chroot && *wp->config->chroot) {
173 if (0 > chroot(wp->config->chroot)) {
174 zlog(ZLOG_SYSERROR, "[pool %s] failed to chroot(%s)", wp->config->name, wp->config->chroot);
175 return -1;
176 }
177 made_chroot = 1;
178 }
179
180 if (wp->config->chdir && *wp->config->chdir) {
181 if (0 > chdir(wp->config->chdir)) {
182 zlog(ZLOG_SYSERROR, "[pool %s] failed to chdir(%s)", wp->config->name, wp->config->chdir);
183 return -1;
184 }
185 } else if (made_chroot) {
186 chdir("/");
187 }
188
189 if (is_root) {
190
191 if (wp->config->process_priority != 64) {
192 if (setpriority(PRIO_PROCESS, 0, wp->config->process_priority) < 0) {
193 zlog(ZLOG_SYSERROR, "[pool %s] Unable to set priority for this new process", wp->config->name);
194 return -1;
195 }
196 }
197
198 if (wp->set_gid) {
199 if (0 > setgid(wp->set_gid)) {
200 zlog(ZLOG_SYSERROR, "[pool %s] failed to setgid(%d)", wp->config->name, wp->set_gid);
201 return -1;
202 }
203 }
204 if (wp->set_uid) {
205 if (0 > initgroups(wp->config->user, wp->set_gid)) {
206 zlog(ZLOG_SYSERROR, "[pool %s] failed to initgroups(%s, %d)", wp->config->name, wp->config->user, wp->set_gid);
207 return -1;
208 }
209 if (0 > setuid(wp->set_uid)) {
210 zlog(ZLOG_SYSERROR, "[pool %s] failed to setuid(%d)", wp->config->name, wp->set_uid);
211 return -1;
212 }
213 }
214 }
215
216 #ifdef HAVE_PRCTL
217 if (0 > prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) {
218 zlog(ZLOG_SYSERROR, "[pool %s] failed to prctl(PR_SET_DUMPABLE)", wp->config->name);
219 }
220 #endif
221
222 if (0 > fpm_clock_init()) {
223 return -1;
224 }
225 return 0;
226 }
227 /* }}} */
228
fpm_unix_init_main()229 int fpm_unix_init_main() /* {{{ */
230 {
231 struct fpm_worker_pool_s *wp;
232 int is_root = !geteuid();
233
234 if (fpm_global_config.rlimit_files) {
235 struct rlimit r;
236
237 r.rlim_max = r.rlim_cur = (rlim_t) fpm_global_config.rlimit_files;
238
239 if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
240 zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", fpm_global_config.rlimit_files);
241 return -1;
242 }
243 }
244
245 if (fpm_global_config.rlimit_core) {
246 struct rlimit r;
247
248 r.rlim_max = r.rlim_cur = fpm_global_config.rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) fpm_global_config.rlimit_core;
249
250 if (0 > setrlimit(RLIMIT_CORE, &r)) {
251 zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", fpm_global_config.rlimit_core);
252 return -1;
253 }
254 }
255
256 fpm_pagesize = getpagesize();
257 if (fpm_global_config.daemonize) {
258 /*
259 * If daemonize, the calling process will die soon
260 * and the master process continues to initialize itself.
261 *
262 * The parent process has then to wait for the master
263 * process to initialize to return a consistent exit
264 * value. For this pupose, the master process will
265 * send \"1\" into the pipe if everything went well
266 * and \"0\" otherwise.
267 */
268
269
270 struct timeval tv;
271 fd_set rfds;
272 int ret;
273
274 if (pipe(fpm_globals.send_config_pipe) == -1) {
275 zlog(ZLOG_SYSERROR, "failed to create pipe");
276 return -1;
277 }
278
279 /* then fork */
280 pid_t pid = fork();
281 switch (pid) {
282
283 case -1 : /* error */
284 zlog(ZLOG_SYSERROR, "failed to daemonize");
285 return -1;
286
287 case 0 : /* children */
288 close(fpm_globals.send_config_pipe[0]); /* close the read side of the pipe */
289 break;
290
291 default : /* parent */
292 close(fpm_globals.send_config_pipe[1]); /* close the write side of the pipe */
293
294 /*
295 * wait for 10s before exiting with error
296 * the child is supposed to send 1 or 0 into the pipe to tell the parent
297 * how it goes for it
298 */
299 FD_ZERO(&rfds);
300 FD_SET(fpm_globals.send_config_pipe[0], &rfds);
301
302 tv.tv_sec = 10;
303 tv.tv_usec = 0;
304
305 zlog(ZLOG_DEBUG, "The calling process is waiting for the master process to ping via fd=%d", fpm_globals.send_config_pipe[0]);
306 ret = select(fpm_globals.send_config_pipe[0] + 1, &rfds, NULL, NULL, &tv);
307 if (ret == -1) {
308 zlog(ZLOG_SYSERROR, "failed to select");
309 exit(FPM_EXIT_SOFTWARE);
310 }
311 if (ret) { /* data available */
312 int readval;
313 ret = read(fpm_globals.send_config_pipe[0], &readval, sizeof(readval));
314 if (ret == -1) {
315 zlog(ZLOG_SYSERROR, "failed to read from pipe");
316 exit(FPM_EXIT_SOFTWARE);
317 }
318
319 if (ret == 0) {
320 zlog(ZLOG_ERROR, "no data have been read from pipe");
321 exit(FPM_EXIT_SOFTWARE);
322 } else {
323 if (readval == 1) {
324 zlog(ZLOG_DEBUG, "I received a valid acknoledge from the master process, I can exit without error");
325 fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);
326 exit(FPM_EXIT_OK);
327 } else {
328 zlog(ZLOG_DEBUG, "The master process returned an error !");
329 exit(FPM_EXIT_SOFTWARE);
330 }
331 }
332 } else { /* no date sent ! */
333 zlog(ZLOG_ERROR, "the master process didn't send back its status (via the pipe to the calling process)");
334 exit(FPM_EXIT_SOFTWARE);
335 }
336 exit(FPM_EXIT_SOFTWARE);
337 }
338 }
339
340 /* continue as a child */
341 setsid();
342 if (0 > fpm_clock_init()) {
343 return -1;
344 }
345
346 if (fpm_global_config.process_priority != 64) {
347 if (is_root) {
348 if (setpriority(PRIO_PROCESS, 0, fpm_global_config.process_priority) < 0) {
349 zlog(ZLOG_SYSERROR, "Unable to set priority for the master process");
350 return -1;
351 }
352 } else {
353 zlog(ZLOG_NOTICE, "'process.priority' directive is ignored when FPM is not running as root");
354 }
355 }
356
357 fpm_globals.parent_pid = getpid();
358 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
359 if (0 > fpm_unix_conf_wp(wp)) {
360 return -1;
361 }
362 }
363
364 zlog_set_level(fpm_globals.log_level);
365 return 0;
366 }
367 /* }}} */
368
369