xref: /PHP-5.5/sapi/fpm/fpm/fpm_unix.c (revision 9e8d4a1b)
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 	return 0;
365 }
366 /* }}} */
367 
368