/src/httpd/os/unix/unixd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include "ap_config.h" |
18 | | #include "httpd.h" |
19 | | #include "http_config.h" |
20 | | #include "http_main.h" |
21 | | #include "http_core.h" |
22 | | #include "http_log.h" |
23 | | #include "unixd.h" |
24 | | #include "mpm_common.h" |
25 | | #include "os.h" |
26 | | #include "ap_mpm.h" |
27 | | #include "apr_thread_proc.h" |
28 | | #include "apr_signal.h" |
29 | | #include "apr_strings.h" |
30 | | #include "apr_portable.h" |
31 | | #ifdef HAVE_PWD_H |
32 | | #include <pwd.h> |
33 | | #endif |
34 | | #ifdef HAVE_SYS_RESOURCE_H |
35 | | #include <sys/resource.h> |
36 | | #endif |
37 | | /* XXX */ |
38 | | #include <sys/stat.h> |
39 | | #ifdef HAVE_UNISTD_H |
40 | | #include <unistd.h> |
41 | | #endif |
42 | | #ifdef HAVE_GRP_H |
43 | | #include <grp.h> |
44 | | #endif |
45 | | #ifdef HAVE_STRINGS_H |
46 | | #include <strings.h> |
47 | | #endif |
48 | | #ifdef HAVE_SYS_SEM_H |
49 | | #include <sys/sem.h> |
50 | | #endif |
51 | | #ifdef HAVE_SYS_PRCTL_H |
52 | | #include <sys/prctl.h> |
53 | | #endif |
54 | | |
55 | | unixd_config_rec ap_unixd_config; |
56 | | |
57 | | APLOG_USE_MODULE(core); |
58 | | |
59 | | AP_DECLARE(void) ap_unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit, |
60 | | const char *arg, |
61 | | const char * arg2, int type) |
62 | 0 | { |
63 | 0 | #if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT |
64 | 0 | char *str; |
65 | 0 | struct rlimit *limit; |
66 | | /* If your platform doesn't define rlim_t then typedef it in ap_config.h */ |
67 | 0 | rlim_t cur = 0; |
68 | 0 | rlim_t max = 0; |
69 | |
|
70 | 0 | *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit)); |
71 | 0 | limit = *plimit; |
72 | 0 | if ((getrlimit(type, limit)) != 0) { |
73 | 0 | *plimit = NULL; |
74 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server, APLOGNO(02172) |
75 | 0 | "%s: getrlimit failed", cmd->cmd->name); |
76 | 0 | return; |
77 | 0 | } |
78 | | |
79 | 0 | if (*(str = ap_getword_conf(cmd->temp_pool, &arg)) != '\0') { |
80 | 0 | if (!strcasecmp(str, "max")) { |
81 | 0 | cur = limit->rlim_max; |
82 | 0 | } |
83 | 0 | else { |
84 | 0 | cur = atol(str); |
85 | 0 | } |
86 | 0 | } |
87 | 0 | else { |
88 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, APLOGNO(02173) |
89 | 0 | "Invalid parameters for %s", cmd->cmd->name); |
90 | 0 | return; |
91 | 0 | } |
92 | | |
93 | 0 | if (arg2 && (*(str = ap_getword_conf(cmd->temp_pool, &arg2)) != '\0')) { |
94 | 0 | max = atol(str); |
95 | 0 | } |
96 | | |
97 | | /* if we aren't running as root, cannot increase max */ |
98 | 0 | if (geteuid()) { |
99 | 0 | limit->rlim_cur = cur; |
100 | 0 | if (max && (max > limit->rlim_max)) { |
101 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, APLOGNO(02174) |
102 | 0 | "Must be uid 0 to raise maximum %s", cmd->cmd->name); |
103 | 0 | } |
104 | 0 | else if (max) { |
105 | 0 | limit->rlim_max = max; |
106 | 0 | } |
107 | 0 | } |
108 | 0 | else { |
109 | 0 | if (cur) { |
110 | 0 | limit->rlim_cur = cur; |
111 | 0 | } |
112 | 0 | if (max) { |
113 | 0 | limit->rlim_max = max; |
114 | 0 | } |
115 | 0 | } |
116 | | #else |
117 | | |
118 | | ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, APLOGNO(02175) |
119 | | "Platform does not support rlimit for %s", cmd->cmd->name); |
120 | | #endif |
121 | 0 | } |
122 | | |
123 | | APR_HOOK_STRUCT( |
124 | | APR_HOOK_LINK(get_suexec_identity) |
125 | | ) |
126 | | |
127 | | AP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity, |
128 | | (const request_rec *r), (r), NULL) |
129 | | |
130 | | static apr_status_t ap_unix_create_privileged_process( |
131 | | apr_proc_t *newproc, const char *progname, |
132 | | const char * const *args, |
133 | | const char * const *env, |
134 | | apr_procattr_t *attr, ap_unix_identity_t *ugid, |
135 | | apr_pool_t *p) |
136 | 0 | { |
137 | 0 | int i = 0; |
138 | 0 | const char **newargs; |
139 | 0 | char *newprogname; |
140 | 0 | char *execuser, *execgroup; |
141 | 0 | const char *argv0; |
142 | |
|
143 | 0 | if (!ap_unixd_config.suexec_enabled) { |
144 | 0 | return apr_proc_create(newproc, progname, args, env, attr, p); |
145 | 0 | } |
146 | | |
147 | 0 | argv0 = ap_strrchr_c(progname, '/'); |
148 | | /* Allow suexec's "/" check to succeed */ |
149 | 0 | if (argv0 != NULL) { |
150 | 0 | argv0++; |
151 | 0 | } |
152 | 0 | else { |
153 | 0 | argv0 = progname; |
154 | 0 | } |
155 | | |
156 | |
|
157 | 0 | if (ugid->userdir) { |
158 | 0 | execuser = apr_psprintf(p, "~%ld", (long) ugid->uid); |
159 | 0 | } |
160 | 0 | else { |
161 | 0 | execuser = apr_psprintf(p, "%ld", (long) ugid->uid); |
162 | 0 | } |
163 | 0 | execgroup = apr_psprintf(p, "%ld", (long) ugid->gid); |
164 | |
|
165 | 0 | if (!execuser || !execgroup) { |
166 | 0 | return APR_ENOMEM; |
167 | 0 | } |
168 | | |
169 | 0 | i = 0; |
170 | 0 | while (args[i]) |
171 | 0 | i++; |
172 | | /* allocate space for 4 new args, the input args, and a null terminator */ |
173 | 0 | newargs = apr_palloc(p, sizeof(char *) * (i + 4)); |
174 | 0 | newprogname = SUEXEC_BIN; |
175 | 0 | newargs[0] = SUEXEC_BIN; |
176 | 0 | newargs[1] = execuser; |
177 | 0 | newargs[2] = execgroup; |
178 | 0 | newargs[3] = apr_pstrdup(p, argv0); |
179 | | |
180 | | /* |
181 | | ** using a shell to execute suexec makes no sense thus |
182 | | ** we force everything to be APR_PROGRAM, and never |
183 | | ** APR_SHELLCMD |
184 | | */ |
185 | 0 | if (apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) { |
186 | 0 | return APR_EGENERAL; |
187 | 0 | } |
188 | | |
189 | 0 | i = 1; |
190 | 0 | do { |
191 | 0 | newargs[i + 3] = args[i]; |
192 | 0 | } while (args[i++]); |
193 | |
|
194 | 0 | return apr_proc_create(newproc, newprogname, newargs, env, attr, p); |
195 | 0 | } |
196 | | |
197 | | AP_DECLARE(apr_status_t) ap_os_create_privileged_process( |
198 | | const request_rec *r, |
199 | | apr_proc_t *newproc, const char *progname, |
200 | | const char * const *args, |
201 | | const char * const *env, |
202 | | apr_procattr_t *attr, apr_pool_t *p) |
203 | 0 | { |
204 | 0 | ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r); |
205 | |
|
206 | 0 | if (ugid == NULL) { |
207 | 0 | return apr_proc_create(newproc, progname, args, env, attr, p); |
208 | 0 | } |
209 | | |
210 | 0 | return ap_unix_create_privileged_process(newproc, progname, args, env, |
211 | 0 | attr, ugid, p); |
212 | 0 | } |
213 | | |
214 | | /* XXX move to APR and externalize (but implement differently :) ) */ |
215 | | static apr_lockmech_e proc_mutex_mech(apr_proc_mutex_t *pmutex) |
216 | 0 | { |
217 | 0 | const char *mechname = apr_proc_mutex_name(pmutex); |
218 | |
|
219 | 0 | if (!strcmp(mechname, "sysvsem")) { |
220 | 0 | return APR_LOCK_SYSVSEM; |
221 | 0 | } |
222 | 0 | else if (!strcmp(mechname, "flock")) { |
223 | 0 | return APR_LOCK_FLOCK; |
224 | 0 | } |
225 | 0 | return APR_LOCK_DEFAULT; |
226 | 0 | } |
227 | | |
228 | | AP_DECLARE(apr_status_t) ap_unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex) |
229 | 0 | { |
230 | 0 | if (!geteuid()) { |
231 | 0 | apr_lockmech_e mech = proc_mutex_mech(pmutex); |
232 | |
|
233 | 0 | switch(mech) { |
234 | 0 | #if APR_HAS_SYSVSEM_SERIALIZE |
235 | 0 | case APR_LOCK_SYSVSEM: |
236 | 0 | { |
237 | 0 | apr_os_proc_mutex_t ospmutex; |
238 | 0 | #if !APR_HAVE_UNION_SEMUN |
239 | 0 | union semun { |
240 | 0 | long val; |
241 | 0 | struct semid_ds *buf; |
242 | 0 | unsigned short *array; |
243 | 0 | }; |
244 | 0 | #endif |
245 | 0 | union semun ick; |
246 | 0 | struct semid_ds buf = { { 0 } }; |
247 | |
|
248 | 0 | apr_os_proc_mutex_get(&ospmutex, pmutex); |
249 | 0 | buf.sem_perm.uid = ap_unixd_config.user_id; |
250 | 0 | buf.sem_perm.gid = ap_unixd_config.group_id; |
251 | 0 | buf.sem_perm.mode = 0600; |
252 | 0 | ick.buf = &buf; |
253 | 0 | if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) { |
254 | 0 | return errno; |
255 | 0 | } |
256 | 0 | } |
257 | 0 | break; |
258 | 0 | #endif |
259 | 0 | #if APR_HAS_FLOCK_SERIALIZE |
260 | 0 | case APR_LOCK_FLOCK: |
261 | 0 | { |
262 | 0 | const char *lockfile = apr_proc_mutex_lockfile(pmutex); |
263 | |
|
264 | 0 | if (lockfile) { |
265 | 0 | if (chown(lockfile, ap_unixd_config.user_id, |
266 | 0 | -1 /* no gid change */) < 0) { |
267 | 0 | return errno; |
268 | 0 | } |
269 | 0 | } |
270 | 0 | } |
271 | 0 | break; |
272 | 0 | #endif |
273 | 0 | default: |
274 | | /* do nothing */ |
275 | 0 | break; |
276 | 0 | } |
277 | 0 | } |
278 | 0 | return APR_SUCCESS; |
279 | 0 | } |
280 | | |
281 | | AP_DECLARE(apr_status_t) ap_unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) |
282 | 0 | { |
283 | 0 | #if !APR_PROC_MUTEX_IS_GLOBAL |
284 | 0 | apr_os_global_mutex_t osgmutex; |
285 | 0 | apr_os_global_mutex_get(&osgmutex, gmutex); |
286 | 0 | return ap_unixd_set_proc_mutex_perms(osgmutex.proc_mutex); |
287 | | #else /* APR_PROC_MUTEX_IS_GLOBAL */ |
288 | | /* In this case, apr_proc_mutex_t and apr_global_mutex_t are the same. */ |
289 | | return ap_unixd_set_proc_mutex_perms(gmutex); |
290 | | #endif /* APR_PROC_MUTEX_IS_GLOBAL */ |
291 | 0 | } |
292 | | |
293 | | AP_DECLARE(apr_status_t) ap_unixd_accept(void **accepted, ap_listen_rec *lr, |
294 | | apr_pool_t *ptrans) |
295 | 0 | { |
296 | 0 | apr_socket_t *csd; |
297 | 0 | apr_status_t status; |
298 | | #ifdef _OSD_POSIX |
299 | | int sockdes; |
300 | | #endif |
301 | |
|
302 | 0 | *accepted = NULL; |
303 | 0 | status = apr_socket_accept(&csd, lr->sd, ptrans); |
304 | 0 | if (status == APR_SUCCESS) { |
305 | 0 | *accepted = csd; |
306 | | #ifdef _OSD_POSIX |
307 | | apr_os_sock_get(&sockdes, csd); |
308 | | if (sockdes >= FD_SETSIZE) { |
309 | | ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(02176) |
310 | | "new file descriptor %d is too large; you probably need " |
311 | | "to rebuild Apache with a larger FD_SETSIZE " |
312 | | "(currently %d)", |
313 | | sockdes, FD_SETSIZE); |
314 | | apr_socket_close(csd); |
315 | | return APR_EINTR; |
316 | | } |
317 | | #endif |
318 | 0 | return APR_SUCCESS; |
319 | 0 | } |
320 | | |
321 | 0 | if (APR_STATUS_IS_EINTR(status)) { |
322 | 0 | return status; |
323 | 0 | } |
324 | | |
325 | | /* Let the caller handle slightly more varied return values */ |
326 | 0 | if ((lr->flags & AP_LISTEN_SPECIFIC_ERRORS) && ap_accept_error_is_nonfatal(status)) { |
327 | 0 | return status; |
328 | 0 | } |
329 | | |
330 | | /* Our old behaviour here was to continue after accept() |
331 | | * errors. But this leads us into lots of troubles |
332 | | * because most of the errors are quite fatal. For |
333 | | * example, EMFILE can be caused by slow descriptor |
334 | | * leaks (say in a 3rd party module, or libc). It's |
335 | | * foolish for us to continue after an EMFILE. We also |
336 | | * seem to tickle kernel bugs on some platforms which |
337 | | * lead to never-ending loops here. So it seems best |
338 | | * to just exit in most cases. |
339 | | */ |
340 | 0 | switch (status) { |
341 | | #if defined(HPUX11) && defined(ENOBUFS) |
342 | | /* On HPUX 11.x, the 'ENOBUFS, No buffer space available' |
343 | | * error occurs because the accept() cannot complete. |
344 | | * You will not see ENOBUFS with 10.20 because the kernel |
345 | | * hides any occurrence from being returned to user space. |
346 | | * ENOBUFS with 11.x's TCP/IP stack is possible, and could |
347 | | * occur intermittently. As a work-around, we are going to |
348 | | * ignore ENOBUFS. |
349 | | */ |
350 | | case ENOBUFS: |
351 | | #endif |
352 | | |
353 | 0 | #ifdef EPROTO |
354 | | /* EPROTO on certain older kernels really means |
355 | | * ECONNABORTED, so we need to ignore it for them. |
356 | | * See discussion in new-httpd archives nh.9701 |
357 | | * search for EPROTO. |
358 | | * |
359 | | * Also see nh.9603, search for EPROTO: |
360 | | * There is potentially a bug in Solaris 2.x x<6, |
361 | | * and other boxes that implement tcp sockets in |
362 | | * userland (i.e. on top of STREAMS). On these |
363 | | * systems, EPROTO can actually result in a fatal |
364 | | * loop. See PR#981 for example. It's hard to |
365 | | * handle both uses of EPROTO. |
366 | | */ |
367 | 0 | case EPROTO: |
368 | 0 | #endif |
369 | 0 | #ifdef ECONNABORTED |
370 | 0 | case ECONNABORTED: |
371 | 0 | #endif |
372 | | /* Linux generates the rest of these, other tcp |
373 | | * stacks (i.e. bsd) tend to hide them behind |
374 | | * getsockopt() interfaces. They occur when |
375 | | * the net goes sour or the client disconnects |
376 | | * after the three-way handshake has been done |
377 | | * in the kernel but before userland has picked |
378 | | * up the socket. |
379 | | */ |
380 | 0 | #ifdef ECONNRESET |
381 | 0 | case ECONNRESET: |
382 | 0 | #endif |
383 | 0 | #ifdef ETIMEDOUT |
384 | 0 | case ETIMEDOUT: |
385 | 0 | #endif |
386 | 0 | #ifdef EHOSTUNREACH |
387 | 0 | case EHOSTUNREACH: |
388 | 0 | #endif |
389 | 0 | #ifdef ENETUNREACH |
390 | 0 | case ENETUNREACH: |
391 | 0 | #endif |
392 | | /* EAGAIN/EWOULDBLOCK can be returned on BSD-derived |
393 | | * TCP stacks when the connection is aborted before |
394 | | * we call connect, but only because our listener |
395 | | * sockets are non-blocking (AP_NONBLOCK_WHEN_MULTI_LISTEN) |
396 | | */ |
397 | 0 | #ifdef EAGAIN |
398 | 0 | case EAGAIN: |
399 | 0 | #endif |
400 | 0 | #ifdef EWOULDBLOCK |
401 | | #if !defined(EAGAIN) || EAGAIN != EWOULDBLOCK |
402 | | case EWOULDBLOCK: |
403 | | #endif |
404 | 0 | #endif |
405 | 0 | break; |
406 | 0 | #ifdef ENETDOWN |
407 | 0 | case ENETDOWN: |
408 | | /* |
409 | | * When the network layer has been shut down, there |
410 | | * is not much use in simply exiting: the parent |
411 | | * would simply re-create us (and we'd fail again). |
412 | | * Use the CHILDFATAL code to tear the server down. |
413 | | * @@@ Martin's idea for possible improvement: |
414 | | * A different approach would be to define |
415 | | * a new APEXIT_NETDOWN exit code, the reception |
416 | | * of which would make the parent shutdown all |
417 | | * children, then idle-loop until it detected that |
418 | | * the network is up again, and restart the children. |
419 | | * Ben Hyde noted that temporary ENETDOWN situations |
420 | | * occur in mobile IP. |
421 | | */ |
422 | 0 | ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, APLOGNO(02177) |
423 | 0 | "apr_socket_accept: giving up."); |
424 | 0 | return APR_EGENERAL; |
425 | 0 | #endif /*ENETDOWN*/ |
426 | | |
427 | 0 | default: |
428 | | /* If the socket has been closed in ap_close_listeners() |
429 | | * by the restart/stop action, we may get EBADF. |
430 | | * Do not print an error in this case. |
431 | | */ |
432 | 0 | if (!lr->active) { |
433 | 0 | ap_log_error(APLOG_MARK, APLOG_DEBUG, status, ap_server_conf, APLOGNO(02178) |
434 | 0 | "apr_socket_accept failed for inactive listener"); |
435 | 0 | return status; |
436 | 0 | } |
437 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf, APLOGNO(02179) |
438 | 0 | "apr_socket_accept: (client socket)"); |
439 | 0 | return APR_EGENERAL; |
440 | 0 | } |
441 | 0 | return status; |
442 | 0 | } |
443 | | |
444 | | |
445 | | /* Unixes MPMs' */ |
446 | | |
447 | | static ap_unixd_mpm_retained_data *retained_data = NULL; |
448 | | static apr_status_t retained_data_cleanup(void *unused) |
449 | 0 | { |
450 | 0 | (void)unused; |
451 | 0 | retained_data = NULL; |
452 | 0 | return APR_SUCCESS; |
453 | 0 | } |
454 | | |
455 | | AP_DECLARE(ap_unixd_mpm_retained_data *) ap_unixd_mpm_get_retained_data() |
456 | 0 | { |
457 | 0 | if (!retained_data) { |
458 | 0 | retained_data = ap_retained_data_create("ap_unixd_mpm_retained_data", |
459 | 0 | sizeof(*retained_data)); |
460 | 0 | apr_pool_pre_cleanup_register(ap_pglobal, NULL, retained_data_cleanup); |
461 | 0 | retained_data->mpm_state = AP_MPMQ_STARTING; |
462 | 0 | } |
463 | 0 | return retained_data; |
464 | 0 | } |
465 | | |
466 | | static void sig_term(int sig) |
467 | 0 | { |
468 | 0 | if (!retained_data) { |
469 | | /* Main process (ap_pglobal) is dying */ |
470 | 0 | return; |
471 | 0 | } |
472 | 0 | if (retained_data->shutdown_pending |
473 | 0 | && (retained_data->is_ungraceful |
474 | 0 | || sig == AP_SIG_GRACEFUL_STOP)) { |
475 | | /* Already handled */ |
476 | 0 | return; |
477 | 0 | } |
478 | | |
479 | 0 | retained_data->mpm_state = AP_MPMQ_STOPPING; |
480 | 0 | retained_data->shutdown_pending = 1; |
481 | 0 | if (sig != AP_SIG_GRACEFUL_STOP) { |
482 | 0 | retained_data->is_ungraceful = 1; |
483 | 0 | } |
484 | 0 | } |
485 | | |
486 | | static void sig_restart(int sig) |
487 | 0 | { |
488 | 0 | if (!retained_data) { |
489 | | /* Main process (ap_pglobal) is dying */ |
490 | 0 | return; |
491 | 0 | } |
492 | 0 | if (retained_data->restart_pending |
493 | 0 | && (retained_data->is_ungraceful |
494 | 0 | || sig == AP_SIG_GRACEFUL)) { |
495 | | /* Already handled */ |
496 | 0 | return; |
497 | 0 | } |
498 | | |
499 | 0 | retained_data->mpm_state = AP_MPMQ_STOPPING; |
500 | 0 | retained_data->restart_pending = 1; |
501 | 0 | if (sig != AP_SIG_GRACEFUL) { |
502 | 0 | retained_data->is_ungraceful = 1; |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | static apr_status_t unset_signals(void *unused) |
507 | 0 | { |
508 | 0 | if (!retained_data) { |
509 | | /* Main process (ap_pglobal) is dying */ |
510 | 0 | return APR_SUCCESS; |
511 | 0 | } |
512 | 0 | retained_data->shutdown_pending = retained_data->restart_pending = 0; |
513 | 0 | retained_data->was_graceful = !retained_data->is_ungraceful; |
514 | 0 | retained_data->is_ungraceful = 0; |
515 | |
|
516 | 0 | return APR_SUCCESS; |
517 | 0 | } |
518 | | |
519 | | static void ap_terminate(void) |
520 | 0 | { |
521 | 0 | ap_main_state = AP_SQ_MS_EXITING; |
522 | 0 | apr_pool_destroy(ap_pglobal); |
523 | 0 | apr_terminate(); |
524 | 0 | } |
525 | | |
526 | | AP_DECLARE(void) ap_unixd_mpm_set_signals(apr_pool_t *pconf, int one_process) |
527 | 0 | { |
528 | 0 | #ifndef NO_USE_SIGACTION |
529 | 0 | struct sigaction sa; |
530 | 0 | #endif |
531 | |
|
532 | 0 | if (!one_process) { |
533 | 0 | ap_fatal_signal_setup(ap_server_conf, pconf); |
534 | 0 | } |
535 | 0 | else if (!ap_retained_data_get("ap_unixd_mpm_one_process_cleanup")) { |
536 | | /* In one process mode (debug), httpd will exit immediately when asked |
537 | | * to (SIGTERM/SIGINT) and never restart. We still want the cleanups to |
538 | | * run though (such that e.g. temporary files/IPCs don't leak on the |
539 | | * system), so the first time around we use atexit() to cleanup after |
540 | | * ourselves. |
541 | | */ |
542 | 0 | ap_retained_data_create("ap_unixd_mpm_one_process_cleanup", 1); |
543 | 0 | atexit(ap_terminate); |
544 | 0 | } |
545 | | |
546 | | /* Signals' handlers depend on retained data */ |
547 | 0 | (void)ap_unixd_mpm_get_retained_data(); |
548 | |
|
549 | 0 | #ifndef NO_USE_SIGACTION |
550 | 0 | memset(&sa, 0, sizeof sa); |
551 | 0 | sigemptyset(&sa.sa_mask); |
552 | |
|
553 | 0 | #ifdef SIGPIPE |
554 | 0 | sa.sa_handler = SIG_IGN; |
555 | 0 | if (sigaction(SIGPIPE, &sa, NULL) < 0) |
556 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00269) |
557 | 0 | "sigaction(SIGPIPE)"); |
558 | 0 | #endif |
559 | 0 | #ifdef SIGXCPU |
560 | 0 | sa.sa_handler = SIG_DFL; |
561 | 0 | if (sigaction(SIGXCPU, &sa, NULL) < 0) |
562 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00267) |
563 | 0 | "sigaction(SIGXCPU)"); |
564 | 0 | #endif |
565 | 0 | #ifdef SIGXFSZ |
566 | | /* For systems following the LFS standard, ignoring SIGXFSZ allows |
567 | | * a write() beyond the 2GB limit to fail gracefully with E2BIG |
568 | | * rather than terminate the process. */ |
569 | 0 | sa.sa_handler = SIG_IGN; |
570 | 0 | if (sigaction(SIGXFSZ, &sa, NULL) < 0) |
571 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00268) |
572 | 0 | "sigaction(SIGXFSZ)"); |
573 | 0 | #endif |
574 | |
|
575 | 0 | sa.sa_handler = sig_term; |
576 | 0 | if (sigaction(SIGTERM, &sa, NULL) < 0) |
577 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00264) |
578 | 0 | "sigaction(SIGTERM)"); |
579 | 0 | #ifdef SIGINT |
580 | 0 | if (sigaction(SIGINT, &sa, NULL) < 0) |
581 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00266) |
582 | 0 | "sigaction(SIGINT)"); |
583 | 0 | #endif |
584 | 0 | #ifdef AP_SIG_GRACEFUL_STOP |
585 | 0 | if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0) |
586 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00265) |
587 | 0 | "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")"); |
588 | 0 | #endif |
589 | | |
590 | | /* Don't catch restart signals in ONE_PROCESS mode :) */ |
591 | 0 | if (!one_process) { |
592 | 0 | sa.sa_handler = sig_restart; |
593 | 0 | if (sigaction(SIGHUP, &sa, NULL) < 0) |
594 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00270) |
595 | 0 | "sigaction(SIGHUP)"); |
596 | 0 | if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0) |
597 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00271) |
598 | 0 | "sigaction(" AP_SIG_GRACEFUL_STRING ")"); |
599 | 0 | } |
600 | |
|
601 | | #else /* NO_USE_SIGACTION */ |
602 | | |
603 | | #ifdef SIGPIPE |
604 | | apr_signal(SIGPIPE, SIG_IGN); |
605 | | #endif /* SIGPIPE */ |
606 | | #ifdef SIGXCPU |
607 | | apr_signal(SIGXCPU, SIG_DFL); |
608 | | #endif /* SIGXCPU */ |
609 | | #ifdef SIGXFSZ |
610 | | apr_signal(SIGXFSZ, SIG_IGN); |
611 | | #endif /* SIGXFSZ */ |
612 | | |
613 | | apr_signal(SIGTERM, sig_term); |
614 | | #ifdef AP_SIG_GRACEFUL_STOP |
615 | | apr_signal(AP_SIG_GRACEFUL_STOP, sig_term); |
616 | | #endif /* AP_SIG_GRACEFUL_STOP */ |
617 | | |
618 | | if (!one_process) { |
619 | | /* Don't restart in ONE_PROCESS mode :) */ |
620 | | #ifdef SIGHUP |
621 | | apr_signal(SIGHUP, sig_restart); |
622 | | #endif /* SIGHUP */ |
623 | | #ifdef AP_SIG_GRACEFUL |
624 | | apr_signal(AP_SIG_GRACEFUL, sig_restart); |
625 | | #endif /* AP_SIG_GRACEFUL */ |
626 | | } |
627 | | |
628 | | #endif /* NO_USE_SIGACTION */ |
629 | |
|
630 | 0 | apr_pool_cleanup_register(pconf, NULL, unset_signals, |
631 | 0 | apr_pool_cleanup_null); |
632 | 0 | } |
633 | | |
634 | | |
635 | | #ifdef _OSD_POSIX |
636 | | |
637 | | #include "apr_lib.h" |
638 | | |
639 | | #define USER_LEN 8 |
640 | | |
641 | | typedef enum |
642 | | { |
643 | | bs2_unknown, /* not initialized yet. */ |
644 | | bs2_noFORK, /* no fork() because -X flag was specified */ |
645 | | bs2_FORK, /* only fork() because uid != 0 */ |
646 | | bs2_UFORK /* Normally, ufork() is used to switch identities. */ |
647 | | } bs2_ForkType; |
648 | | |
649 | | static bs2_ForkType forktype = bs2_unknown; |
650 | | |
651 | | /* Determine the method for forking off a child in such a way as to |
652 | | * set both the POSIX and BS2000 user id's to the unprivileged user. |
653 | | */ |
654 | | static bs2_ForkType os_forktype(int one_process) |
655 | | { |
656 | | /* have we checked the OS version before? If yes return the previous |
657 | | * result - the OS release isn't going to change suddenly! |
658 | | */ |
659 | | if (forktype == bs2_unknown) { |
660 | | /* not initialized yet */ |
661 | | |
662 | | /* No fork if the one_process option was set */ |
663 | | if (one_process) { |
664 | | forktype = bs2_noFORK; |
665 | | } |
666 | | /* If the user is unprivileged, use the normal fork() only. */ |
667 | | else if (getuid() != 0) { |
668 | | forktype = bs2_FORK; |
669 | | } |
670 | | else |
671 | | forktype = bs2_UFORK; |
672 | | } |
673 | | return forktype; |
674 | | } |
675 | | |
676 | | |
677 | | |
678 | | /* This routine complements the setuid() call: it causes the BS2000 job |
679 | | * environment to be switched to the target user's user id. |
680 | | * That is important if CGI scripts try to execute native BS2000 commands. |
681 | | */ |
682 | | int os_init_job_environment(server_rec *server, const char *user_name, int one_process) |
683 | | { |
684 | | bs2_ForkType type = os_forktype(one_process); |
685 | | |
686 | | /* We can be sure that no change to uid==0 is possible because of |
687 | | * the checks in http_core.c:set_user() |
688 | | */ |
689 | | |
690 | | if (one_process) { |
691 | | |
692 | | type = forktype = bs2_noFORK; |
693 | | |
694 | | ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, APLOGNO(02180) |
695 | | "The debug mode of Apache should only " |
696 | | "be started by an unprivileged user!"); |
697 | | return 0; |
698 | | } |
699 | | |
700 | | return 0; |
701 | | } |
702 | | |
703 | | /* BS2000 requires a "special" version of fork() before a setuid() call */ |
704 | | pid_t os_fork(const char *user) |
705 | | { |
706 | | pid_t pid; |
707 | | char username[USER_LEN+1]; |
708 | | |
709 | | switch (os_forktype(0)) { |
710 | | |
711 | | case bs2_FORK: |
712 | | pid = fork(); |
713 | | break; |
714 | | |
715 | | case bs2_UFORK: |
716 | | apr_cpystrn(username, user, sizeof username); |
717 | | |
718 | | /* Make user name all upper case - for some versions of ufork() */ |
719 | | ap_str_toupper(username); |
720 | | |
721 | | pid = ufork(username); |
722 | | if (pid == -1 && errno == EPERM) { |
723 | | ap_log_error(APLOG_MARK, APLOG_EMERG, errno, ap_server_conf, |
724 | | APLOGNO(02181) "ufork: Possible mis-configuration " |
725 | | "for user %s - Aborting.", user); |
726 | | exit(1); |
727 | | } |
728 | | break; |
729 | | |
730 | | default: |
731 | | pid = 0; |
732 | | break; |
733 | | } |
734 | | |
735 | | return pid; |
736 | | } |
737 | | |
738 | | #endif /* _OSD_POSIX */ |
739 | | |