in modules/fcgid/fcgid_proc_unix.c [194:400]
apr_status_t proc_spawn_process(const char *cmdline, fcgid_proc_info *procinfo,
fcgid_procnode *procnode)
{
server_rec *main_server = procinfo->main_server;
fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config,
&fcgid_module);
apr_status_t rv = APR_SUCCESS;
apr_file_t *file;
apr_proc_t tmpproc;
int omask, retcode, unix_socket;
char **proc_environ;
struct sockaddr_un unix_addr;
apr_procattr_t *procattr = NULL;
int len;
const char **wargv;
/* Build wrapper args */
apr_tokenize_to_argv(cmdline, (char ***)&wargv, procnode->proc_pool);
/*
Create UNIX domain socket before spawn
*/
/* Generate a UNIX domain socket file path */
memset(&unix_addr, 0, sizeof(unix_addr));
unix_addr.sun_family = AF_UNIX;
len = apr_snprintf(unix_addr.sun_path, sizeof(unix_addr.sun_path),
"%s/%" APR_PID_T_FMT ".%d", sconf->sockname_prefix,
getpid(), g_process_counter++);
/* check for truncation of the socket path
*
* cheap but overly zealous check for sun_path overflow: if length of
* prepared string is at the limit, assume truncation
*/
if (len + 1 == sizeof(unix_addr.sun_path)
|| len >= sizeof procnode->socket_path) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"mod_fcgid: socket path length exceeds compiled-in limits");
return APR_EGENERAL;
}
apr_cpystrn(procnode->socket_path, unix_addr.sun_path,
sizeof(procnode->socket_path));
/* truncation already checked for in handler or FcgidWrapper parser */
AP_DEBUG_ASSERT(wargv[0] != NULL);
AP_DEBUG_ASSERT(strlen(wargv[0]) < sizeof(procnode->executable_path));
apr_cpystrn(procnode->executable_path, wargv[0],
sizeof(procnode->executable_path));
/* Unlink the file just in case */
unlink(unix_addr.sun_path);
/* Create the socket */
if ((unix_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
"mod_fcgid: couldn't create unix domain socket");
return errno;
}
/* Register cleanups to
* 1. Unlink the socket when the process exits
* 2. (suexec mode only, in the child cleanup) Switch to the configured uid
*/
if (ap_unixd_config.suexec_enabled) {
apr_pool_cleanup_register(procnode->proc_pool,
procnode, socket_file_cleanup,
exec_setuid_cleanup);
}
else {
apr_pool_cleanup_register(procnode->proc_pool,
procnode, socket_file_cleanup,
apr_pool_cleanup_null);
}
/* Bind the socket */
omask = umask(0077);
retcode = bind(unix_socket, (struct sockaddr *) &unix_addr,
sizeof(unix_addr));
umask(omask);
if (retcode < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
"mod_fcgid: couldn't bind unix domain socket %s",
unix_addr.sun_path);
close(unix_socket);
return errno;
}
/* IPC directory permissions are safe, but avoid confusion */
/* Not all flavors of unix use the current umask for AF_UNIX perms */
rv = apr_file_perms_set(unix_addr.sun_path,
APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server,
"mod_fcgid: Couldn't set permissions on unix domain socket %s",
unix_addr.sun_path);
return rv;
}
/* Listen the socket */
if (listen(unix_socket, DEFAULT_FCGID_LISTENBACKLOG) < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
"mod_fcgid: couldn't listen on unix domain socket");
close(unix_socket);
return errno;
}
/* Correct the file owner */
if (!geteuid()) {
if (chown(unix_addr.sun_path, ap_unixd_config.user_id, -1) < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
"mod_fcgid: couldn't change owner of unix domain socket %s",
unix_addr.sun_path);
close(unix_socket);
return errno;
}
}
{
int oldflags = fcntl(unix_socket, F_GETFD, 0);
if (oldflags < 0) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
procinfo->main_server,
"mod_fcgid: fcntl F_GETFD failed");
close(unix_socket);
return errno;
}
oldflags |= FD_CLOEXEC;
if (fcntl(unix_socket, F_SETFD, oldflags) < 0) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
procinfo->main_server,
"mod_fcgid: fcntl F_SETFD failed");
close(unix_socket);
return errno;
}
}
/* Build environment variables */
proc_environ = ap_create_environment(procnode->proc_pool,
procinfo->proc_environ);
if (!proc_environ) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
procinfo->main_server,
"mod_fcgid: can't build environment variables");
close(unix_socket);
return APR_ENOMEM;
}
/* Prepare the fork */
if ((rv = apr_procattr_create(&procattr, procnode->proc_pool)) != APR_SUCCESS
|| (rv = apr_procattr_child_err_set(procattr,
procinfo->main_server->error_log,
NULL)) != APR_SUCCESS
|| (rv = apr_procattr_child_out_set(procattr,
procinfo->main_server->error_log,
NULL)) != APR_SUCCESS
|| (rv = apr_procattr_dir_set(procattr,
ap_make_dirstr_parent(procnode->proc_pool,
wargv[0]))) != APR_SUCCESS
|| (rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS
|| (rv = apr_os_file_put(&file, &unix_socket, 0,
procnode->proc_pool)) != APR_SUCCESS
|| (rv = apr_procattr_child_in_set(procattr, file, NULL)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server,
"mod_fcgid: couldn't set child process attributes: %s",
unix_addr.sun_path);
close(unix_socket);
return rv;
}
/* fork and exec now */
/* Note, don't pass &(procnode->proc_id) to fcgid_create_privileged_process(),
* for it's a share memory address, both parent and child process may modify
* procnode->proc_id->pid, so sometimes it's 0 and sometimes it's >0
*/
rv = fcgid_create_privileged_process(&tmpproc, wargv[0], wargv,
(const char *const *)proc_environ,
procattr, procinfo,
procnode->proc_pool);
if (ap_unixd_config.suexec_enabled) {
/* Prior to creating the child process, a child cleanup was registered
* to switch the uid in the child. No-op the child cleanup for this
* pool so that it won't run again as other child processes are created.
* (The cleanup will be registered for the pool associated with those
* processes too.)
*/
apr_pool_child_cleanup_set(procnode->proc_pool, procnode,
socket_file_cleanup, apr_pool_cleanup_null);
}
/* Close socket before try to connect to it */
close(unix_socket);
procnode->proc_id = tmpproc;
if (rv != APR_SUCCESS) {
memset(&procnode->proc_id, 0, sizeof(procnode->proc_id));
ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server,
"mod_fcgid: can't run %s", wargv[0]);
}
return rv;
}