|
Packit Service |
f9aed3 |
/*
|
|
Packit Service |
f9aed3 |
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit Service |
f9aed3 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit Service |
f9aed3 |
* this work for additional information regarding copyright ownership.
|
|
Packit Service |
f9aed3 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit Service |
f9aed3 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit Service |
f9aed3 |
* the License. You may obtain a copy of the License at
|
|
Packit Service |
f9aed3 |
*
|
|
Packit Service |
f9aed3 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit Service |
f9aed3 |
*
|
|
Packit Service |
f9aed3 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit Service |
f9aed3 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit Service |
f9aed3 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit Service |
f9aed3 |
* See the License for the specific language governing permissions and
|
|
Packit Service |
f9aed3 |
* limitations under the License.
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
#include <unistd.h>
|
|
Packit Service |
f9aed3 |
#include <sys/un.h>
|
|
Packit Service |
f9aed3 |
#include <sys/types.h>
|
|
Packit Service |
f9aed3 |
#include <netinet/tcp.h> /* For TCP_NODELAY */
|
|
Packit Service |
f9aed3 |
#include <sys/poll.h>
|
|
Packit Service |
f9aed3 |
#include <sys/stat.h>
|
|
Packit Service |
f9aed3 |
#define CORE_PRIVATE
|
|
Packit Service |
f9aed3 |
#include "httpd.h"
|
|
Packit Service |
f9aed3 |
#include "apr_version.h"
|
|
Packit Service |
f9aed3 |
#include "apr_thread_proc.h"
|
|
Packit Service |
f9aed3 |
#include "apr_strings.h"
|
|
Packit Service |
f9aed3 |
#include "apr_portable.h"
|
|
Packit Service |
f9aed3 |
#include "apr_pools.h"
|
|
Packit Service |
f9aed3 |
#include "apr_network_io.h"
|
|
Packit Service |
f9aed3 |
#include "ap_mpm.h"
|
|
Packit Service |
f9aed3 |
#include "http_config.h"
|
|
Packit Service |
f9aed3 |
#include "mpm_common.h"
|
|
Packit Service |
f9aed3 |
#include "util_script.h"
|
|
Packit Service |
f9aed3 |
#include "unixd.h"
|
|
Packit Service |
f9aed3 |
#include "mod_core.h"
|
|
Packit Service |
f9aed3 |
#include "mod_cgi.h"
|
|
Packit Service |
f9aed3 |
#include "apr_tables.h"
|
|
Packit Service |
f9aed3 |
#include "fcgid_proc.h"
|
|
Packit Service |
f9aed3 |
#include "fcgid_proctbl.h"
|
|
Packit Service |
f9aed3 |
#include "fcgid_protocol.h"
|
|
Packit Service |
f9aed3 |
#include "fcgid_conf.h"
|
|
Packit Service |
f9aed3 |
#include "fcgid_pm.h"
|
|
Packit Service |
f9aed3 |
#include "fcgid_spawn_ctl.h"
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
#if MODULE_MAGIC_NUMBER_MAJOR < 20081201
|
|
Packit Service |
f9aed3 |
#define ap_unixd_config unixd_config
|
|
Packit Service |
f9aed3 |
#endif
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
#if APR_MAJOR_VERSION < 1
|
|
Packit Service |
f9aed3 |
#define APR_FPROT_UWRITE APR_UWRITE
|
|
Packit Service |
f9aed3 |
#define APR_FPROT_UREAD APR_UREAD
|
|
Packit Service |
f9aed3 |
#define APR_FPROT_UEXECUTE APR_UEXECUTE
|
|
Packit Service |
f9aed3 |
#endif
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
#define DEFAULT_FCGID_LISTENBACKLOG 5
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
typedef struct {
|
|
Packit Service |
f9aed3 |
int handle_socket;
|
|
Packit Service |
f9aed3 |
} fcgid_namedpipe_handle;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static int g_process_counter = 0;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t ap_unix_create_privileged_process(apr_proc_t *newproc,
|
|
Packit Service |
f9aed3 |
const char *progname,
|
|
Packit Service |
f9aed3 |
const char *const *args,
|
|
Packit Service |
f9aed3 |
const char *const *env,
|
|
Packit Service |
f9aed3 |
apr_procattr_t *attr,
|
|
Packit Service |
f9aed3 |
ap_unix_identity_t *ugid,
|
|
Packit Service |
f9aed3 |
apr_pool_t *p)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
int i = 0;
|
|
Packit Service |
f9aed3 |
const char **newargs;
|
|
Packit Service |
f9aed3 |
const char *newprogname;
|
|
Packit Service |
f9aed3 |
const char *execuser, *execgroup;
|
|
Packit Service |
f9aed3 |
const char *argv0;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (!ap_unixd_config.suexec_enabled) {
|
|
Packit Service |
f9aed3 |
return apr_proc_create(newproc, progname, args, env, attr, p);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
argv0 = ap_strrchr_c(progname, '/');
|
|
Packit Service |
f9aed3 |
/* Allow suexec's "/" check to succeed */
|
|
Packit Service |
f9aed3 |
if (argv0 != NULL) {
|
|
Packit Service |
f9aed3 |
argv0++;
|
|
Packit Service |
f9aed3 |
} else {
|
|
Packit Service |
f9aed3 |
argv0 = progname;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (ugid->userdir) {
|
|
Packit Service |
f9aed3 |
execuser = apr_psprintf(p, "~%ld", (long) ugid->uid);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else {
|
|
Packit Service |
f9aed3 |
execuser = apr_psprintf(p, "%ld", (long) ugid->uid);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
execgroup = apr_psprintf(p, "%ld", (long) ugid->gid);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (!execuser || !execgroup) {
|
|
Packit Service |
f9aed3 |
return APR_ENOMEM;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
i = 0;
|
|
Packit Service |
f9aed3 |
while (args[i]) {
|
|
Packit Service |
f9aed3 |
i++;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
/* allocate space for 4 new args, the input args, and a null terminator */
|
|
Packit Service |
f9aed3 |
newargs = apr_palloc(p, sizeof(char *) * (i + 4));
|
|
Packit Service |
f9aed3 |
newprogname = SUEXEC_BIN;
|
|
Packit Service |
f9aed3 |
newargs[0] = SUEXEC_BIN;
|
|
Packit Service |
f9aed3 |
newargs[1] = execuser;
|
|
Packit Service |
f9aed3 |
newargs[2] = execgroup;
|
|
Packit Service |
f9aed3 |
newargs[3] = apr_pstrdup(p, argv0);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/*
|
|
Packit Service |
f9aed3 |
** using a shell to execute suexec makes no sense thus
|
|
Packit Service |
f9aed3 |
** we force everything to be APR_PROGRAM, and never
|
|
Packit Service |
f9aed3 |
** APR_SHELLCMD
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
if (apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) {
|
|
Packit Service |
f9aed3 |
return APR_EGENERAL;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
i = 1;
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
newargs[i + 3] = args[i];
|
|
Packit Service |
f9aed3 |
} while (args[i++]);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return apr_proc_create(newproc, newprogname, newargs, env, attr, p);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t fcgid_create_privileged_process(apr_proc_t *newproc,
|
|
Packit Service |
f9aed3 |
const char *progname,
|
|
Packit Service |
f9aed3 |
const char *const *args,
|
|
Packit Service |
f9aed3 |
const char *const *env,
|
|
Packit Service |
f9aed3 |
apr_procattr_t *attr,
|
|
Packit Service |
f9aed3 |
fcgid_proc_info *procinfo,
|
|
Packit Service |
f9aed3 |
apr_pool_t *p)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
ap_unix_identity_t ugid;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (!ap_unixd_config.suexec_enabled
|
|
Packit Service |
f9aed3 |
|| (procinfo->uid == (uid_t) - 1
|
|
Packit Service |
f9aed3 |
&& procinfo->gid == (gid_t) - 1)) {
|
|
Packit Service |
f9aed3 |
return apr_proc_create(newproc, progname, args, env, attr, p);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
ugid.gid = procinfo->gid;
|
|
Packit Service |
f9aed3 |
ugid.uid = procinfo->uid;
|
|
Packit Service |
f9aed3 |
ugid.userdir = procinfo->userdir;
|
|
Packit Service |
f9aed3 |
return ap_unix_create_privileged_process(newproc, progname, args, env,
|
|
Packit Service |
f9aed3 |
attr, &ugid, p);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t socket_file_cleanup(void *theprocnode)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
fcgid_procnode *procnode = (fcgid_procnode *) theprocnode;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
unlink(procnode->socket_path);
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static void log_setid_failure(const char *proc_type,
|
|
Packit Service |
f9aed3 |
const char *id_type,
|
|
Packit Service |
f9aed3 |
uid_t user_id)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
char errno_desc[120];
|
|
Packit Service |
f9aed3 |
char errmsg[240];
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_strerror(errno, errno_desc, sizeof errno_desc);
|
|
Packit Service |
f9aed3 |
apr_snprintf(errmsg, sizeof errmsg,
|
|
Packit Service |
f9aed3 |
"(%d)%s: %s unable to set %s to %ld\n",
|
|
Packit Service |
f9aed3 |
errno, errno_desc, proc_type, id_type, (long)user_id);
|
|
Packit Service |
f9aed3 |
write(STDERR_FILENO, errmsg, strlen(errmsg));
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* When suexec is enabled, this runs in the forked child
|
|
Packit Service |
f9aed3 |
* process prior to exec().
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
static apr_status_t exec_setuid_cleanup(void *dummy)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
if (seteuid(0) == -1) {
|
|
Packit Service |
f9aed3 |
log_setid_failure("mod_fcgid child", "effective uid", 0);
|
|
Packit Service |
f9aed3 |
_exit(1);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
if (setuid(ap_unixd_config.user_id) == -1) {
|
|
Packit Service |
f9aed3 |
log_setid_failure("mod_fcgid child", "uid", ap_unixd_config.user_id);
|
|
Packit Service |
f9aed3 |
_exit(1);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_spawn_process(const char *cmdline, fcgid_proc_info *procinfo,
|
|
Packit Service |
f9aed3 |
fcgid_procnode *procnode)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
server_rec *main_server = procinfo->main_server;
|
|
Packit Service |
f9aed3 |
fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config,
|
|
Packit Service |
f9aed3 |
&fcgid_module);
|
|
Packit Service |
f9aed3 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
apr_file_t *file;
|
|
Packit Service |
f9aed3 |
apr_proc_t tmpproc;
|
|
Packit Service |
f9aed3 |
int omask, retcode, unix_socket;
|
|
Packit Service |
f9aed3 |
char **proc_environ;
|
|
Packit Service |
f9aed3 |
struct sockaddr_un unix_addr;
|
|
Packit Service |
f9aed3 |
apr_procattr_t *procattr = NULL;
|
|
Packit Service |
f9aed3 |
int len;
|
|
Packit Service |
f9aed3 |
const char **wargv;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Build wrapper args */
|
|
Packit Service |
f9aed3 |
apr_tokenize_to_argv(cmdline, (char ***)&wargv, procnode->proc_pool);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/*
|
|
Packit Service |
f9aed3 |
Create UNIX domain socket before spawn
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Generate a UNIX domain socket file path */
|
|
Packit Service |
f9aed3 |
memset(&unix_addr, 0, sizeof(unix_addr));
|
|
Packit Service |
f9aed3 |
unix_addr.sun_family = AF_UNIX;
|
|
Packit Service |
f9aed3 |
len = apr_snprintf(unix_addr.sun_path, sizeof(unix_addr.sun_path),
|
|
Packit Service |
f9aed3 |
"%s/%" APR_PID_T_FMT ".%d", sconf->sockname_prefix,
|
|
Packit Service |
f9aed3 |
getpid(), g_process_counter++);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* check for truncation of the socket path
|
|
Packit Service |
f9aed3 |
*
|
|
Packit Service |
f9aed3 |
* cheap but overly zealous check for sun_path overflow: if length of
|
|
Packit Service |
f9aed3 |
* prepared string is at the limit, assume truncation
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
if (len + 1 == sizeof(unix_addr.sun_path)
|
|
Packit Service |
f9aed3 |
|| len >= sizeof procnode->socket_path) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: socket path length exceeds compiled-in limits");
|
|
Packit Service |
f9aed3 |
return APR_EGENERAL;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_cpystrn(procnode->socket_path, unix_addr.sun_path,
|
|
Packit Service |
f9aed3 |
sizeof(procnode->socket_path));
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* truncation already checked for in handler or FcgidWrapper parser */
|
|
Packit Service |
f9aed3 |
AP_DEBUG_ASSERT(wargv[0] != NULL);
|
|
Packit Service |
f9aed3 |
AP_DEBUG_ASSERT(strlen(wargv[0]) < sizeof(procnode->executable_path));
|
|
Packit Service |
f9aed3 |
apr_cpystrn(procnode->executable_path, wargv[0],
|
|
Packit Service |
f9aed3 |
sizeof(procnode->executable_path));
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Unlink the file just in case */
|
|
Packit Service |
f9aed3 |
unlink(unix_addr.sun_path);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Create the socket */
|
|
Packit Service |
f9aed3 |
if ((unix_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: couldn't create unix domain socket");
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Register cleanups to
|
|
Packit Service |
f9aed3 |
* 1. Unlink the socket when the process exits
|
|
Packit Service |
f9aed3 |
* 2. (suexec mode only, in the child cleanup) Switch to the configured uid
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
if (ap_unixd_config.suexec_enabled) {
|
|
Packit Service |
f9aed3 |
apr_pool_cleanup_register(procnode->proc_pool,
|
|
Packit Service |
f9aed3 |
procnode, socket_file_cleanup,
|
|
Packit Service |
f9aed3 |
exec_setuid_cleanup);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else {
|
|
Packit Service |
f9aed3 |
apr_pool_cleanup_register(procnode->proc_pool,
|
|
Packit Service |
f9aed3 |
procnode, socket_file_cleanup,
|
|
Packit Service |
f9aed3 |
apr_pool_cleanup_null);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Bind the socket */
|
|
Packit Service |
f9aed3 |
omask = umask(0077);
|
|
Packit Service |
f9aed3 |
retcode = bind(unix_socket, (struct sockaddr *) &unix_addr,
|
|
Packit Service |
f9aed3 |
sizeof(unix_addr));
|
|
Packit Service |
f9aed3 |
umask(omask);
|
|
Packit Service |
f9aed3 |
if (retcode < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: couldn't bind unix domain socket %s",
|
|
Packit Service |
f9aed3 |
unix_addr.sun_path);
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* IPC directory permissions are safe, but avoid confusion */
|
|
Packit Service |
f9aed3 |
/* Not all flavors of unix use the current umask for AF_UNIX perms */
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
rv = apr_file_perms_set(unix_addr.sun_path,
|
|
Packit Service |
f9aed3 |
APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE);
|
|
Packit Service |
f9aed3 |
if (rv != APR_SUCCESS) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: Couldn't set permissions on unix domain socket %s",
|
|
Packit Service |
f9aed3 |
unix_addr.sun_path);
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Listen the socket */
|
|
Packit Service |
f9aed3 |
if (listen(unix_socket, DEFAULT_FCGID_LISTENBACKLOG) < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: couldn't listen on unix domain socket");
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Correct the file owner */
|
|
Packit Service |
f9aed3 |
if (!geteuid()) {
|
|
Packit Service |
f9aed3 |
if (chown(unix_addr.sun_path, ap_unixd_config.user_id, -1) < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: couldn't change owner of unix domain socket %s",
|
|
Packit Service |
f9aed3 |
unix_addr.sun_path);
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
int oldflags = fcntl(unix_socket, F_GETFD, 0);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (oldflags < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
|
|
Packit Service |
f9aed3 |
procinfo->main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: fcntl F_GETFD failed");
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
oldflags |= FD_CLOEXEC;
|
|
Packit Service |
f9aed3 |
if (fcntl(unix_socket, F_SETFD, oldflags) < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
|
|
Packit Service |
f9aed3 |
procinfo->main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: fcntl F_SETFD failed");
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Build environment variables */
|
|
Packit Service |
f9aed3 |
proc_environ = ap_create_environment(procnode->proc_pool,
|
|
Packit Service |
f9aed3 |
procinfo->proc_environ);
|
|
Packit Service |
f9aed3 |
if (!proc_environ) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
|
|
Packit Service |
f9aed3 |
procinfo->main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: can't build environment variables");
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return APR_ENOMEM;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Prepare the fork */
|
|
Packit Service |
f9aed3 |
if ((rv = apr_procattr_create(&procattr, procnode->proc_pool)) != APR_SUCCESS
|
|
Packit Service |
f9aed3 |
|| (rv = apr_procattr_child_err_set(procattr,
|
|
Packit Service |
f9aed3 |
procinfo->main_server->error_log,
|
|
Packit Service |
f9aed3 |
NULL)) != APR_SUCCESS
|
|
Packit Service |
f9aed3 |
|| (rv = apr_procattr_child_out_set(procattr,
|
|
Packit Service |
f9aed3 |
procinfo->main_server->error_log,
|
|
Packit Service |
f9aed3 |
NULL)) != APR_SUCCESS
|
|
Packit Service |
f9aed3 |
|| (rv = apr_procattr_dir_set(procattr,
|
|
Packit Service |
f9aed3 |
ap_make_dirstr_parent(procnode->proc_pool,
|
|
Packit Service |
f9aed3 |
wargv[0]))) != APR_SUCCESS
|
|
Packit Service |
f9aed3 |
|| (rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS
|
|
Packit Service |
f9aed3 |
|| (rv = apr_os_file_put(&file, &unix_socket, 0,
|
|
Packit Service |
f9aed3 |
procnode->proc_pool)) != APR_SUCCESS
|
|
Packit Service |
f9aed3 |
|| (rv = apr_procattr_child_in_set(procattr, file, NULL)) != APR_SUCCESS) {
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: couldn't set child process attributes: %s",
|
|
Packit Service |
f9aed3 |
unix_addr.sun_path);
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* fork and exec now */
|
|
Packit Service |
f9aed3 |
/* Note, don't pass &(procnode->proc_id) to fcgid_create_privileged_process(),
|
|
Packit Service |
f9aed3 |
* for it's a share memory address, both parent and child process may modify
|
|
Packit Service |
f9aed3 |
* procnode->proc_id->pid, so sometimes it's 0 and sometimes it's >0
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
rv = fcgid_create_privileged_process(&tmpproc, wargv[0], wargv,
|
|
Packit Service |
f9aed3 |
(const char *const *)proc_environ,
|
|
Packit Service |
f9aed3 |
procattr, procinfo,
|
|
Packit Service |
f9aed3 |
procnode->proc_pool);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (ap_unixd_config.suexec_enabled) {
|
|
Packit Service |
f9aed3 |
/* Prior to creating the child process, a child cleanup was registered
|
|
Packit Service |
f9aed3 |
* to switch the uid in the child. No-op the child cleanup for this
|
|
Packit Service |
f9aed3 |
* pool so that it won't run again as other child processes are created.
|
|
Packit Service |
f9aed3 |
* (The cleanup will be registered for the pool associated with those
|
|
Packit Service |
f9aed3 |
* processes too.)
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
apr_pool_child_cleanup_set(procnode->proc_pool, procnode,
|
|
Packit Service |
f9aed3 |
socket_file_cleanup, apr_pool_cleanup_null);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Close socket before try to connect to it */
|
|
Packit Service |
f9aed3 |
close(unix_socket);
|
|
Packit Service |
f9aed3 |
procnode->proc_id = tmpproc;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (rv != APR_SUCCESS) {
|
|
Packit Service |
f9aed3 |
memset(&procnode->proc_id, 0, sizeof(procnode->proc_id));
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: can't run %s", wargv[0]);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t proc_kill_internal(fcgid_procnode *procnode, int sig)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
/* su as root before sending signal, for suEXEC */
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (procnode->proc_id.pid == 0) {
|
|
Packit Service |
f9aed3 |
/* procnode->proc_id.pid be 0 while fcgid_create_privileged_process() fail */
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (ap_unixd_config.suexec_enabled && seteuid(0) != 0) {
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* can't gain privileges to send signal (should not occur); do NOT
|
|
Packit Service |
f9aed3 |
* proceed, as something is broken with current identity
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
log_setid_failure("mod_fcgid PM", "effective uid", 0);
|
|
Packit Service |
f9aed3 |
_exit(1);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
rv = apr_proc_kill(&(procnode->proc_id), sig);
|
|
Packit Service |
f9aed3 |
if (ap_unixd_config.suexec_enabled && seteuid(ap_unixd_config.user_id) != 0) {
|
|
Packit Service |
f9aed3 |
/* can't drop privileges after signalling (should not occur); do NOT
|
|
Packit Service |
f9aed3 |
* proceed any further as euid(0)!
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
log_setid_failure("mod_fcgid PM", "effective uid", ap_unixd_config.user_id);
|
|
Packit Service |
f9aed3 |
_exit(1);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_kill_gracefully(fcgid_procnode *procnode, server_rec *main_server)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
return proc_kill_internal(procnode, SIGTERM);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_kill_force(fcgid_procnode * procnode,
|
|
Packit Service |
f9aed3 |
server_rec * main_server)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
return proc_kill_internal(procnode, SIGKILL);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_wait_process(server_rec *main_server, fcgid_procnode *procnode)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
int exitcode;
|
|
Packit Service |
f9aed3 |
apr_exit_why_e exitwhy;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
rv = apr_proc_wait(&(procnode->proc_id), &exitcode, &exitwhy, APR_NOWAIT);
|
|
Packit Service |
f9aed3 |
if (rv == APR_CHILD_DONE || rv == APR_EGENERAL) {
|
|
Packit Service |
f9aed3 |
/* Log why and how it die */
|
|
Packit Service |
f9aed3 |
proc_print_exit_info(procnode, exitcode, exitwhy, main_server);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Register the death */
|
|
Packit Service |
f9aed3 |
register_termination(main_server, procnode);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Destroy pool */
|
|
Packit Service |
f9aed3 |
apr_pool_destroy(procnode->proc_pool);
|
|
Packit Service |
f9aed3 |
procnode->proc_pool = NULL;
|
|
Packit Service |
f9aed3 |
memset(&procnode->proc_id, 0, sizeof(procnode->proc_id));
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return APR_CHILD_DONE;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t ipc_handle_cleanup(void *thesocket)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
fcgid_namedpipe_handle *handle_info =
|
|
Packit Service |
f9aed3 |
(fcgid_namedpipe_handle *) thesocket;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (handle_info) {
|
|
Packit Service |
f9aed3 |
if (handle_info->handle_socket != -1) {
|
|
Packit Service |
f9aed3 |
close(handle_info->handle_socket);
|
|
Packit Service |
f9aed3 |
handle_info->handle_socket = -1;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t set_socket_nonblock(int sd)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
#ifndef BEOS
|
|
Packit Service |
f9aed3 |
int fd_flags;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
fd_flags = fcntl(sd, F_GETFL, 0);
|
|
Packit Service |
f9aed3 |
#if defined(O_NONBLOCK)
|
|
Packit Service |
f9aed3 |
fd_flags |= O_NONBLOCK;
|
|
Packit Service |
f9aed3 |
#elif defined(O_NDELAY)
|
|
Packit Service |
f9aed3 |
fd_flags |= O_NDELAY;
|
|
Packit Service |
f9aed3 |
#elif defined(FNDELAY)
|
|
Packit Service |
f9aed3 |
fd_flags |= FNDELAY;
|
|
Packit Service |
f9aed3 |
#else
|
|
Packit Service |
f9aed3 |
#error Please teach APR how to make sockets non-blocking on your platform.
|
|
Packit Service |
f9aed3 |
#endif
|
|
Packit Service |
f9aed3 |
if (fcntl(sd, F_SETFL, fd_flags) == -1) {
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
#else
|
|
Packit Service |
f9aed3 |
int on = 1;
|
|
Packit Service |
f9aed3 |
if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
#endif /* BEOS */
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_connect_ipc(fcgid_procnode *procnode, fcgid_ipc *ipc_handle)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
fcgid_namedpipe_handle *handle_info;
|
|
Packit Service |
f9aed3 |
struct sockaddr_un unix_addr;
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Alloc memory for unix domain socket */
|
|
Packit Service |
f9aed3 |
ipc_handle->ipc_handle_info
|
|
Packit Service |
f9aed3 |
= (fcgid_namedpipe_handle *) apr_pcalloc(ipc_handle->request->pool,
|
|
Packit Service |
f9aed3 |
sizeof
|
|
Packit Service |
f9aed3 |
(fcgid_namedpipe_handle));
|
|
Packit Service |
f9aed3 |
handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info;
|
|
Packit Service |
f9aed3 |
handle_info->handle_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
Packit Service |
f9aed3 |
apr_pool_cleanup_register(ipc_handle->request->pool,
|
|
Packit Service |
f9aed3 |
handle_info, ipc_handle_cleanup,
|
|
Packit Service |
f9aed3 |
apr_pool_cleanup_null);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Connect to fastcgi server */
|
|
Packit Service |
f9aed3 |
memset(&unix_addr, 0, sizeof(unix_addr));
|
|
Packit Service |
f9aed3 |
unix_addr.sun_family = AF_UNIX;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* PM already made this check for truncation */
|
|
Packit Service |
f9aed3 |
AP_DEBUG_ASSERT(sizeof unix_addr.sun_path > strlen(procnode->socket_path));
|
|
Packit Service |
f9aed3 |
apr_cpystrn(unix_addr.sun_path, procnode->socket_path,
|
|
Packit Service |
f9aed3 |
sizeof(unix_addr.sun_path));
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* I am the only one who connecting the server
|
|
Packit Service |
f9aed3 |
So I don't have to worry about ECONNREFUSED(listen queue overflow) problem,
|
|
Packit Service |
f9aed3 |
and I will never retry on error */
|
|
Packit Service |
f9aed3 |
if (connect(handle_info->handle_socket, (struct sockaddr *) &unix_addr,
|
|
Packit Service |
f9aed3 |
sizeof(unix_addr)) < 0) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, apr_get_os_error(),
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: can't connect unix domain socket: %s",
|
|
Packit Service |
f9aed3 |
procnode->socket_path);
|
|
Packit Service |
f9aed3 |
return APR_ECONNREFUSED;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Set nonblock option */
|
|
Packit Service |
f9aed3 |
if ((rv = set_socket_nonblock(handle_info->handle_socket)) != APR_SUCCESS) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: can't make unix domain socket nonblocking");
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_close_ipc(fcgid_ipc * ipc_handle)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
rv = apr_pool_cleanup_run(ipc_handle->request->pool,
|
|
Packit Service |
f9aed3 |
ipc_handle->ipc_handle_info,
|
|
Packit Service |
f9aed3 |
ipc_handle_cleanup);
|
|
Packit Service |
f9aed3 |
ipc_handle->ipc_handle_info = NULL;
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
apr_status_t proc_read_ipc(fcgid_ipc *ipc_handle, const char *buffer,
|
|
Packit Service |
f9aed3 |
apr_size_t *size)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
int retcode, unix_socket;
|
|
Packit Service |
f9aed3 |
fcgid_namedpipe_handle *handle_info;
|
|
Packit Service |
f9aed3 |
struct pollfd pollfds[1];
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info;
|
|
Packit Service |
f9aed3 |
unix_socket = handle_info->handle_socket;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
if ((retcode = read(unix_socket, (void *) buffer, *size)) > 0) {
|
|
Packit Service |
f9aed3 |
*size = retcode;
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
} while (retcode == -1 && APR_STATUS_IS_EINTR(errno));
|
|
Packit Service |
f9aed3 |
if (retcode == -1 && !APR_STATUS_IS_EAGAIN(errno)) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno,
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: error reading data from FastCGI server");
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* I have to wait a while */
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
pollfds[0].fd = unix_socket;
|
|
Packit Service |
f9aed3 |
pollfds[0].events = POLLIN;
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
retcode = poll(pollfds, 1, ipc_handle->communation_timeout * 1000);
|
|
Packit Service |
f9aed3 |
} while (retcode <= 0 && APR_STATUS_IS_EINTR(errno));
|
|
Packit Service |
f9aed3 |
if (retcode == -1) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno,
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: error polling unix domain socket");
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else if (retcode == 0) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: read data timeout in %d seconds",
|
|
Packit Service |
f9aed3 |
ipc_handle->communation_timeout);
|
|
Packit Service |
f9aed3 |
return APR_ETIMEDOUT;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
if ((retcode = read(unix_socket, (void *) buffer, *size)) > 0) {
|
|
Packit Service |
f9aed3 |
*size = retcode;
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
} while (retcode == -1 && APR_STATUS_IS_EINTR(errno));
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (retcode == 0) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: error reading data, FastCGI server closed connection");
|
|
Packit Service |
f9aed3 |
return APR_EPIPE;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno,
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: error reading data from FastCGI server");
|
|
Packit Service |
f9aed3 |
return errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t socket_writev(fcgid_ipc *ipc_handle,
|
|
Packit Service |
f9aed3 |
struct iovec *vec, int nvec,
|
|
Packit Service |
f9aed3 |
int *writecnt)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
int retcode, unix_socket;
|
|
Packit Service |
f9aed3 |
fcgid_namedpipe_handle *handle_info;
|
|
Packit Service |
f9aed3 |
struct pollfd pollfds[1];
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info;
|
|
Packit Service |
f9aed3 |
unix_socket = handle_info->handle_socket;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Try nonblock write */
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
if ((retcode = writev(unix_socket, vec, nvec)) > 0) {
|
|
Packit Service |
f9aed3 |
*writecnt = retcode;
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
} while (retcode == -1 && APR_STATUS_IS_EINTR(errno));
|
|
Packit Service |
f9aed3 |
rv = errno;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
Packit Service |
f9aed3 |
/* poll() */
|
|
Packit Service |
f9aed3 |
pollfds[0].fd = unix_socket;
|
|
Packit Service |
f9aed3 |
pollfds[0].events = POLLOUT;
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
retcode = poll(pollfds, 1, ipc_handle->communation_timeout * 1000);
|
|
Packit Service |
f9aed3 |
} while (retcode < 0 && APR_STATUS_IS_EINTR(errno));
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (retcode < 0) {
|
|
Packit Service |
f9aed3 |
rv = errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else if (retcode == 0) {
|
|
Packit Service |
f9aed3 |
rv = APR_TIMEUP;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else {
|
|
Packit Service |
f9aed3 |
/* Write again */
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
if ((retcode = writev(unix_socket, vec, nvec)) > 0) {
|
|
Packit Service |
f9aed3 |
*writecnt = retcode;
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
} while (retcode == -1 && APR_STATUS_IS_EINTR(errno));
|
|
Packit Service |
f9aed3 |
rv = errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
Packit Service |
f9aed3 |
/* socket is writable, but we can't write the entire buffer; try to write a
|
|
Packit Service |
f9aed3 |
* smaller amount, and if even that fails then sleep
|
|
Packit Service |
f9aed3 |
*/
|
|
Packit Service |
f9aed3 |
size_t to_write = vec[0].iov_len;
|
|
Packit Service |
f9aed3 |
int slept = 0;
|
|
Packit Service |
f9aed3 |
const apr_interval_time_t sleep_time = APR_USEC_PER_SEC / 4;
|
|
Packit Service |
f9aed3 |
const int max_sleeps = 8;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
do {
|
|
Packit Service |
f9aed3 |
if ((retcode = write(unix_socket, vec[0].iov_base, to_write)) > 0) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"wrote %d byte(s) and slept %d time(s) after EAGAIN",
|
|
Packit Service |
f9aed3 |
retcode, slept);
|
|
Packit Service |
f9aed3 |
*writecnt = retcode;
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
if (APR_STATUS_IS_EAGAIN(errno)) {
|
|
Packit Service |
f9aed3 |
if (to_write == 1) {
|
|
Packit Service |
f9aed3 |
apr_sleep(sleep_time);
|
|
Packit Service |
f9aed3 |
++slept;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else {
|
|
Packit Service |
f9aed3 |
to_write /= 2;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
} while ((APR_STATUS_IS_EINTR(errno) || APR_STATUS_IS_EAGAIN(errno))
|
|
Packit Service |
f9aed3 |
&& slept < max_sleeps);
|
|
Packit Service |
f9aed3 |
rv = errno;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv,
|
|
Packit Service |
f9aed3 |
ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: error writing data to FastCGI server");
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
static apr_status_t writev_it_all(fcgid_ipc *ipc_handle,
|
|
Packit Service |
f9aed3 |
struct iovec *vec, int nvec)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
apr_size_t bytes_written = 0;
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
apr_size_t len = 0;
|
|
Packit Service |
f9aed3 |
int i = 0;
|
|
Packit Service |
f9aed3 |
int writecnt = 0;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Calculate the total size */
|
|
Packit Service |
f9aed3 |
for (i = 0; i < nvec; i++) {
|
|
Packit Service |
f9aed3 |
len += vec[i].iov_len;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
i = 0;
|
|
Packit Service |
f9aed3 |
while (bytes_written != len) {
|
|
Packit Service |
f9aed3 |
rv = socket_writev(ipc_handle, vec + i, nvec - i, &writecnt);
|
|
Packit Service |
f9aed3 |
if (rv != APR_SUCCESS)
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
bytes_written += writecnt;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (bytes_written < len) {
|
|
Packit Service |
f9aed3 |
/* Skip over the vectors that have already been written */
|
|
Packit Service |
f9aed3 |
apr_size_t cnt = vec[i].iov_len;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
while (writecnt >= cnt && i + 1 < nvec) {
|
|
Packit Service |
f9aed3 |
i++;
|
|
Packit Service |
f9aed3 |
cnt += vec[i].iov_len;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (writecnt < cnt) {
|
|
Packit Service |
f9aed3 |
/* Handle partial write of vec i */
|
|
Packit Service |
f9aed3 |
vec[i].iov_base = (char *) vec[i].iov_base +
|
|
Packit Service |
f9aed3 |
(vec[i].iov_len - (cnt - writecnt));
|
|
Packit Service |
f9aed3 |
vec[i].iov_len = cnt - writecnt;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
#define FCGID_VEC_COUNT 8
|
|
Packit Service |
f9aed3 |
apr_status_t proc_write_ipc(fcgid_ipc *ipc_handle,
|
|
Packit Service |
f9aed3 |
apr_bucket_brigade *output_brigade)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
apr_status_t rv;
|
|
Packit Service |
f9aed3 |
struct iovec vec[FCGID_VEC_COUNT];
|
|
Packit Service |
f9aed3 |
int nvec = 0;
|
|
Packit Service |
f9aed3 |
apr_bucket *e;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
af40fa |
for (e = APR_BRIGADE_FIRST(output_brigade);
|
|
Packit Service |
af40fa |
e != APR_BRIGADE_SENTINEL(output_brigade);
|
|
Packit Service |
af40fa |
e = APR_BUCKET_NEXT(e)) {
|
|
Packit Service |
f9aed3 |
apr_size_t len;
|
|
Packit Service |
f9aed3 |
const char* base;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if (APR_BUCKET_IS_METADATA(e)) {
|
|
Packit Service |
f9aed3 |
continue;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
if ((rv = apr_bucket_read(e, &base, &len,
|
|
Packit Service |
f9aed3 |
APR_BLOCK_READ)) != APR_SUCCESS) {
|
|
Packit Service |
f9aed3 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, ipc_handle->request,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: can't read request from bucket");
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
vec[nvec].iov_len = len;
|
|
Packit Service |
f9aed3 |
vec[nvec].iov_base = (char*) base;
|
|
Packit Service |
f9aed3 |
if (nvec == (FCGID_VEC_COUNT - 1)) {
|
|
Packit Service |
f9aed3 |
/* It's time to write now */
|
|
Packit Service |
f9aed3 |
if ((rv =
|
|
Packit Service |
f9aed3 |
writev_it_all(ipc_handle, vec,
|
|
Packit Service |
f9aed3 |
FCGID_VEC_COUNT)) != APR_SUCCESS)
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
nvec = 0;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else
|
|
Packit Service |
f9aed3 |
nvec++;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* There are something left */
|
|
Packit Service |
f9aed3 |
if (nvec != 0) {
|
|
Packit Service |
f9aed3 |
if ((rv = writev_it_all(ipc_handle, vec, nvec)) != APR_SUCCESS)
|
|
Packit Service |
f9aed3 |
return rv;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
return APR_SUCCESS;
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
void proc_print_exit_info(fcgid_procnode *procnode, int exitcode,
|
|
Packit Service |
f9aed3 |
apr_exit_why_e exitwhy, server_rec *main_server)
|
|
Packit Service |
f9aed3 |
{
|
|
Packit Service |
f9aed3 |
const char *diewhy = NULL;
|
|
Packit Service |
f9aed3 |
char signal_info[HUGE_STRING_LEN];
|
|
Packit Service |
f9aed3 |
int signum = exitcode;
|
|
Packit Service |
f9aed3 |
int loglevel = APLOG_INFO;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
memset(signal_info, 0, HUGE_STRING_LEN);
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Reasons to exit */
|
|
Packit Service |
f9aed3 |
switch (procnode->diewhy) {
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_KILLSELF:
|
|
Packit Service |
f9aed3 |
diewhy = "normal exit";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_IDLE_TIMEOUT:
|
|
Packit Service |
f9aed3 |
diewhy = "idle timeout";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_LIFETIME_EXPIRED:
|
|
Packit Service |
f9aed3 |
diewhy = "lifetime expired";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_BUSY_TIMEOUT:
|
|
Packit Service |
f9aed3 |
diewhy = "busy timeout";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_CONNECT_ERROR:
|
|
Packit Service |
f9aed3 |
diewhy = "connect error";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_COMM_ERROR:
|
|
Packit Service |
f9aed3 |
diewhy = "communication error";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
case FCGID_DIE_SHUTDOWN:
|
|
Packit Service |
f9aed3 |
diewhy = "shutting down";
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
default:
|
|
Packit Service |
f9aed3 |
loglevel = APLOG_ERR;
|
|
Packit Service |
f9aed3 |
diewhy = "unknown";
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Get signal info */
|
|
Packit Service |
f9aed3 |
if (APR_PROC_CHECK_SIGNALED(exitwhy)) {
|
|
Packit Service |
f9aed3 |
switch (signum) {
|
|
Packit Service |
f9aed3 |
case SIGTERM:
|
|
Packit Service |
f9aed3 |
case SIGHUP:
|
|
Packit Service |
f9aed3 |
case AP_SIG_GRACEFUL:
|
|
Packit Service |
f9aed3 |
case SIGKILL:
|
|
Packit Service |
f9aed3 |
apr_snprintf(signal_info, HUGE_STRING_LEN - 1,
|
|
Packit Service |
f9aed3 |
"get stop signal %d", signum);
|
|
Packit Service |
f9aed3 |
break;
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
default:
|
|
Packit Service |
f9aed3 |
loglevel = APLOG_ERR;
|
|
Packit Service |
f9aed3 |
if (APR_PROC_CHECK_CORE_DUMP(exitwhy)) {
|
|
Packit Service |
f9aed3 |
apr_snprintf(signal_info, HUGE_STRING_LEN - 1,
|
|
Packit Service |
f9aed3 |
"get signal %d, possible coredump generated",
|
|
Packit Service |
f9aed3 |
signum);
|
|
Packit Service |
f9aed3 |
} else {
|
|
Packit Service |
f9aed3 |
apr_snprintf(signal_info, HUGE_STRING_LEN - 1,
|
|
Packit Service |
f9aed3 |
"get unexpected signal %d", signum);
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
else if (APR_PROC_CHECK_EXIT(exitwhy)) {
|
|
Packit Service |
f9aed3 |
apr_snprintf(signal_info, HUGE_STRING_LEN - 1,
|
|
Packit Service |
f9aed3 |
"terminated by calling exit(), return code: %d",
|
|
Packit Service |
f9aed3 |
exitcode);
|
|
Packit Service |
f9aed3 |
if (procnode->diewhy == FCGID_DIE_CONNECT_ERROR)
|
|
Packit Service |
f9aed3 |
diewhy = "server exited";
|
|
Packit Service |
f9aed3 |
}
|
|
Packit Service |
f9aed3 |
|
|
Packit Service |
f9aed3 |
/* Print log now */
|
|
Packit Service |
f9aed3 |
ap_log_error(APLOG_MARK, loglevel, 0, main_server,
|
|
Packit Service |
f9aed3 |
"mod_fcgid: process %s(%" APR_PID_T_FMT ") exit(%s), %s",
|
|
Packit Service |
f9aed3 |
procnode->executable_path, procnode->proc_id.pid, diewhy, signal_info);
|
|
Packit Service |
f9aed3 |
}
|