Ejemplo n.º 1
0
ret_t
cherokee_shm_map (cherokee_shm_t    *shm,
                  cherokee_buffer_t *name)
{
    int         re;
    int         fd;
    struct stat info;

    fd = shm_open (name->buf, O_RDWR, 0600);
    if (fd < 0) {
        return ret_error;
    }

    re = fstat (fd, &info);
    if (re != 0) {
        return ret_error;
    }

    shm->mem = mmap (0, info.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shm->mem == MAP_FAILED) {
        cherokee_fd_close (fd);
        shm->mem = NULL;
        return ret_error;
    }

    cherokee_fd_close (fd);

    cherokee_buffer_clean      (&shm->name);
    cherokee_buffer_add_buffer (&shm->name, name);

    return ret_ok;
}
Ejemplo n.º 2
0
ret_t
cherokee_shm_create (cherokee_shm_t *shm, char *name, size_t len)
{
    int re;
    int fd;

    fd = shm_open (name, O_RDWR | O_EXCL | O_CREAT, 0600);
    if (fd < 0) {
        return ret_error;
    }

    re = ftruncate (fd, len);
    if (re < 0) {
        cherokee_fd_close (fd);
        return ret_error;
    }

    shm->mem = mmap (0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shm->mem == MAP_FAILED) {
        shm->mem = NULL;
        cherokee_fd_close (fd);
        return ret_error;
    }

    cherokee_fd_close (fd);

    shm->len = len;
    cherokee_buffer_add (&shm->name, name, strlen(name));

    return ret_ok;
}
Ejemplo n.º 3
0
ret_t
cherokee_buffer_read_file (cherokee_buffer_t *buf, char *filename)
{
	int r, f;
	ret_t ret;
	struct stat info;

	/* Stat() the file
	 */
	r = cherokee_stat (filename, &info);
	if (r != 0)
		return ret_error;

	/* Is a regular file?
	 */
	if (S_ISREG(info.st_mode) == 0)
		return ret_error;

	/* Maybe get memory
	 */
	ret = cherokee_buffer_ensure_size (buf, buf->len + info.st_size + 1);
	if (unlikely (ret != ret_ok))
		return ret;

	/* Open the file
	 */
	f = cherokee_open (filename, O_RDONLY | O_BINARY, 0);
	if (f < 0) {
		LOG_ERRNO(errno, cherokee_err_error, CHEROKEE_ERROR_BUFFER_OPEN_FILE, filename);
		return ret_error;
	}

	cherokee_fd_set_closexec (f);

	/* Read the content
	 */
	r = read (f, buf->buf + buf->len, info.st_size);
	if (r < 0) {
		buf->buf[buf->len] = '\0';

		cherokee_fd_close(f);
		return ret_error;
	}

	/* Close it and exit
	 */
	cherokee_fd_close(f);

	buf->len += r;
	buf->buf[buf->len] = '\0';

	return ret_ok;
}
Ejemplo n.º 4
0
ret_t
cherokee_socket_accept_fd (cherokee_socket_t   *server_socket,
                           int                 *new_fd,
                           cherokee_sockaddr_t *sa)
{
	ret_t     ret;
	socklen_t len;
	int       new_socket;

	/* Get the new connection
	 */
	len = sizeof (cherokee_sockaddr_t);

	do {
		new_socket = accept (server_socket->socket, &sa->sa, &len);
	} while ((new_socket == -1) && (errno == EINTR));

	if (new_socket < 0) {
		return ret_error;
	}

	/* It'd nice to be able to reuse the address even if the
	 * socket is still in TIME_WAIT statue (2*RTT ~ 120 seg)
	 */
	cherokee_fd_set_reuseaddr (new_socket);

	/* Close-on-exec: Child processes won't inherit this fd
	 */
	cherokee_fd_set_closexec (new_socket);

	/* Enables nonblocking I/O.
	 */
	ret = cherokee_fd_set_nonblocking (new_socket, true);
	if (ret != ret_ok) {
		LOG_WARNING (CHEROKEE_ERROR_SOCKET_NON_BLOCKING, new_socket);
		cherokee_fd_close (new_socket);
		return ret_error;
	}

	/* Disable Nagle's algorithm for this connection
	 * so that there is no delay involved when sending data
	 * which don't fill up a full IP datagram.
	 */
	ret = cherokee_fd_set_nodelay (new_socket, true);
	if (ret != ret_ok) {
		LOG_WARNING_S (CHEROKEE_ERROR_SOCKET_RM_NAGLES);
		cherokee_fd_close (new_socket);
		return ret_error;
	}

	*new_fd = new_socket;
	return ret_ok;
}
Ejemplo n.º 5
0
ret_t
cherokee_socket_close (cherokee_socket_t *socket)
{
	ret_t ret;

	/* Sanity check
	 */
	if (socket->socket < 0) {
		return ret_error;
	}

	/* Close the socket
	 */
#ifdef _WIN32
	ret = closesocket (socket->socket);
#else
	ret = cherokee_fd_close (socket->socket);
#endif

	/* Clean up
	 */
	TRACE (ENTRIES",close", "fd=%d is_tls=%d re=%d\n",
	       socket->socket, socket->is_tls, (int) ret);

	socket->socket = -1;
	socket->status = socket_closed;
	socket->is_tls = non_TLS;

	return ret;
}
Ejemplo n.º 6
0
static ret_t
launch_logger_process (cherokee_logger_writer_t *writer)
{
#ifdef HAVE_FORK
	int   fd;
	int   to_log_fds[2];
	pid_t pid;

	if (cherokee_pipe (to_log_fds)) {
		LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_LOGGER_WRITER_PIPE, errno);
		return ret_error;
	}

	switch (pid = fork()) {
	case 0:
		/* Child
		 */
		cherokee_fd_close (STDIN_FILENO);
		dup2 (to_log_fds[0], STDIN_FILENO);
		cherokee_fd_close (to_log_fds[0]);
		cherokee_fd_close (to_log_fds[1]);

		for (fd = 3; fd < 256; fd++) {
			cherokee_fd_close (fd);
		}

		do {
			execl("/bin/sh", "sh", "-c", writer->command.buf, NULL);
		} while (errno == EINTR);

		SHOULDNT_HAPPEN;

	case -1:
		LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_LOGGER_WRITER_FORK, errno);
		break;

	default:
		cherokee_fd_close (to_log_fds[0]);
		writer->fd = to_log_fds[1];
	}
#else
	return ret_no_sys;
#endif
	return ret_ok;
}
Ejemplo n.º 7
0
static ret_t
fork_and_execute_cgi_unix (cherokee_handler_cgi_t *cgi)
{
	int                    re;
	int                    pid;
	cherokee_connection_t *conn = HANDLER_CONN(cgi);

	struct {
		int cgi[2];
		int server[2];
	} pipes;

	/* Creates the pipes ...
	 */
	re  = pipe (pipes.cgi);
	re |= pipe (pipes.server);

	if (re != 0) {
		conn->error_code = http_internal_error;
		return ret_error;
	}

	/* .. and fork the process
	 */
	pid = fork();
	if (pid == 0) {
		/* CGI process
		 */
		manage_child_cgi_process (cgi, pipes.cgi, pipes.server);

	} else if (pid < 0) {
		/* Error
		 */
		cherokee_fd_close (pipes.cgi[0]);
		cherokee_fd_close (pipes.cgi[1]);

		cherokee_fd_close (pipes.server[0]);
		cherokee_fd_close (pipes.server[1]);

		conn->error_code = http_internal_error;
		return ret_error;
	}

	TRACE (ENTRIES, "pid %d\n", pid);

	cherokee_fd_close (pipes.server[0]);
	cherokee_fd_close (pipes.cgi[1]);

	cgi->pid        = pid;
	cgi->pipeInput  = pipes.cgi[0];
	cgi->pipeOutput = pipes.server[1];

	/* Set to Input to NON-BLOCKING
	 */
	_fd_set_properties (cgi->pipeInput, O_NDELAY|O_NONBLOCK, 0);

	return ret_ok;
}
Ejemplo n.º 8
0
static ret_t
logger_writer_close_file (cherokee_logger_writer_t *writer)
{
	ret_t ret = ret_ok;

	if (writer->fd != -1) {
		if (writer->type != cherokee_logger_writer_stderr) {
			if (cherokee_fd_close (writer->fd) != 0)
				ret = ret_error;
		}
		writer->fd = -1;
	}

	return ret;
}
Ejemplo n.º 9
0
ret_t
cherokee_handler_cgi_read_post (cherokee_handler_cgi_t *cgi)
{
	ret_t                     ret;
	cherokee_connection_t    *conn     = HANDLER_CONN(cgi);
	cherokee_socket_status_t  blocking = socket_closed;
	cherokee_boolean_t        did_IO   = false;

	if (! conn->post.has_info) {
		return ret_ok;
	}

	ret = cherokee_post_send_to_fd (&conn->post, &conn->socket,
					cgi->pipeOutput, NULL, &blocking, &did_IO);

	if (did_IO) {
		cherokee_connection_update_timeout (conn);
	}

	switch (ret) {
	case ret_ok:
		break;
	case ret_eagain:
		if (blocking == socket_writing) {
			cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi),
							     conn, cgi->pipeOutput,
							     FDPOLL_MODE_WRITE, false);
			return ret_deny;
		}

		/* ret_eagain - Block on read
		 * ret_deny   - Block on back-end write
		 */
		if (cherokee_post_has_buffered_info (&conn->post)) {
			return ret_deny;
		}
		return ret_eagain;

	default:
		return ret;
	}

	TRACE (ENTRIES",post", "%s\n", "finished");
	cherokee_fd_close (cgi->pipeOutput);
	cgi->pipeOutput = -1;

	return ret_ok;
}
Ejemplo n.º 10
0
ret_t
cherokee_socket_accept (cherokee_socket_t *socket, cherokee_socket_t *server_socket)
{
	ret_t               ret;
	int                 fd;
	cherokee_sockaddr_t sa;

	ret = cherokee_socket_accept_fd (server_socket, &fd, &sa);
	if (unlikely(ret < ret_ok))
		return ret;

	ret = cherokee_socket_set_sockaddr (socket, fd, &sa);
	if (unlikely(ret < ret_ok)) {
		cherokee_fd_close (fd);
		SOCKET_FD(socket) = -1;
		return ret;
	}

	return ret_ok;
}
Ejemplo n.º 11
0
ret_t
cherokee_socket_accept_fd (cherokee_socket_t   *server_socket,
			   int                 *new_fd,
			   cherokee_sockaddr_t *sa)
{
	ret_t     ret;
	socklen_t len;
	int       new_socket;

	/* Get the new connection
	 */
	len = sizeof (cherokee_sockaddr_t);

	do {
		new_socket = accept (server_socket->socket, &sa->sa, &len);
	} while ((new_socket == -1) && (errno == EINTR));

	if (new_socket < 0) {
		return ret_error;
	}

#if 0 /* DISABLED */

	/* Deal with the FIN_WAIT2 state
	 */
	re = 1;
	re = setsockopt (new_socket, SOL_SOCKET, SO_KEEPALIVE, &re, sizeof(re));
	if (re == -1) {
		LOG_ERRNO (errno, cherokee_err_warning,
			   CHEROKEE_ERROR_SOCKET_SET_KEEPALIVE, new_socket);
	}

	linger.l_onoff  = 1;
	linger.l_linger = SECONDS_TO_LINGER;

	re = setsockopt (new_socket, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
	if (re == -1) {
		LOG_ERRNO (errno, cherokee_err_warning,
			   CHEROKEE_ERROR_SOCKET_SET_LINGER, new_socket);
	}
#endif

	/* Close-on-exec: Child processes won't inherit this fd
	 */
	cherokee_fd_set_closexec (new_socket);

	/* Enables nonblocking I/O.
	 */
	ret = cherokee_fd_set_nonblocking (new_socket, true);
	if (ret != ret_ok) {
		LOG_WARNING (CHEROKEE_ERROR_SOCKET_NON_BLOCKING, new_socket);

		cherokee_fd_close (new_socket);
		return ret_error;
	}

	/* Disable Nagle's algorithm for this connection
	 * so that there is no delay involved when sending data
	 * which don't fill up a full IP datagram.
	 */
	ret = cherokee_fd_set_nodelay (new_socket, true);
	if (ret != ret_ok) {
		LOG_WARNING_S (CHEROKEE_ERROR_SOCKET_RM_NAGLES);

		cherokee_fd_close (new_socket);
		return ret_error;
	}

	*new_fd = new_socket;
	return ret_ok;
}
Ejemplo n.º 12
0
static NORETURN void
manage_child_cgi_process (cherokee_handler_cgi_t *cgi, int pipe_cgi[2], int pipe_server[2])
{
	/* Child process
	 */
	int                          re;
	char                        *script;
	cherokee_connection_t       *conn          = HANDLER_CONN(cgi);
	cherokee_handler_cgi_base_t *cgi_base      = HDL_CGI_BASE(cgi);
	char                        *absolute_path = cgi_base->executable.buf;
	char                        *argv[2]       = { NULL, NULL };

#ifdef TRACE_ENABLED
	TRACE(ENTRIES, "About to execute: '%s'\n", absolute_path);

	if (! cherokee_buffer_is_empty (&conn->effective_directory))
		TRACE(ENTRIES, "Effective directory: '%s'\n", conn->effective_directory.buf);
	else
		TRACE(ENTRIES, "No Effective directory %s", "\n");
#endif

	/* Close useless sides
	 */
	cherokee_fd_close (pipe_cgi[0]);
	cherokee_fd_close (pipe_server[1]);

	/* Change stdin and out
	 */
	re  = dup2 (pipe_server[0], STDIN_FILENO);
	cherokee_fd_close (pipe_server[0]);

	if (unlikely (re != 0)) {
		printf ("Status: 500" CRLF_CRLF);
		printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__);
		exit(1);
	}

	re |= dup2 (pipe_cgi[1], STDOUT_FILENO);
	cherokee_fd_close (pipe_cgi[1]);

	/* Redirect the stderr
	 */
	if ((CONN_VSRV(conn)->error_writer != NULL) &&
	    (CONN_VSRV(conn)->error_writer->fd != -1))
	{
		dup2 (CONN_VSRV(conn)->error_writer->fd, STDERR_FILENO);
	}

# if 0
	/* Set unbuffered
	 */
	setvbuf (stdin,  NULL, _IONBF, 0);
	setvbuf (stdout, NULL, _IONBF, 0);
# endif

	/* Enable blocking mode
	 */
	_fd_set_properties (STDIN_FILENO,  0, O_NONBLOCK);
	_fd_set_properties (STDOUT_FILENO, 0, O_NONBLOCK);
	_fd_set_properties (STDERR_FILENO, 0, O_NONBLOCK);

	/* Sets the new environ.
	 */
	add_environment (cgi, conn);

	/* Change the directory
	 */
	if (! cherokee_buffer_is_empty (&conn->effective_directory)) {
		re = chdir (conn->effective_directory.buf);
	} else {
		char *file = strrchr (absolute_path, '/');

		*file = '\0';
		re = chdir (absolute_path);
		*file = '/';
	}

	if (re < 0) {
		printf ("Status: 500" CRLF_CRLF);
		printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__);
		exit(1);
	}

	/* Build de argv array
	 */
	script  = absolute_path;
	argv[0] = absolute_path;

	/* Change the execution user?
	 */
	if (HANDLER_CGI_PROPS(cgi_base)->change_user) {
		struct stat info;

		re = stat (script, &info);
		if (re >= 0) {
			re = setuid (info.st_uid);
			if (re != 0) {
				LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_SETID, script, info.st_uid);
			}
		}
	}

	/* Reset the server-wide signal handlers
	 */
#ifdef SIGPIPE
	signal (SIGPIPE, SIG_DFL);
#endif
#ifdef SIGHUP
        signal (SIGHUP,  SIG_DFL);
#endif
#ifdef SIGSEGV
        signal (SIGSEGV, SIG_DFL);
#endif
#ifdef SIGBUS
        signal (SIGBUS, SIG_DFL);
#endif
#ifdef SIGTERM
        signal (SIGTERM, SIG_DFL);
#endif

	/* Lets go.. execute it!
	 */
	re = execve (absolute_path, argv, cgi->envp);
	if (re < 0) {
		int err = errno;
		char buferr[ERROR_MAX_BUFSIZE];

		switch (err) {
		case ENOENT:
			printf ("Status: 404" CRLF_CRLF);
			break;
		default:
			printf ("Status: 500" CRLF_CRLF);
			printf ("X-Debug: file=%s line=%d cmd=%s: %s" CRLF_CRLF,
				__FILE__, __LINE__, absolute_path, strerror(err));
		}

		/* Don't use the logging system (concurrency issues)
		 */
		LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_EXECUTE,
			   absolute_path, cherokee_strerror_r(err, buferr, sizeof(buferr)));
		exit(1);
	}

	/* There is no way, it could reach this point.
	 */
	SHOULDNT_HAPPEN;
	exit(2);
}
Ejemplo n.º 13
0
ret_t
cherokee_handler_cgi_free (cherokee_handler_cgi_t *cgi)
{
 	int i;

	/* Free the rest of the handler CGI memory
	 */
	cherokee_handler_cgi_base_free (HDL_CGI_BASE(cgi));

	/* Close the connection with the CGI
	 */
	if (cgi->pipeInput > 0) {
		cherokee_fd_close (cgi->pipeInput);
		cgi->pipeInput = -1;
	}

	if (cgi->pipeOutput > 0) {
		cherokee_fd_close (cgi->pipeOutput);
		cgi->pipeOutput = -1;
	}

        /* Kill the CGI
	 */
#ifndef _WIN32
	if (cgi->pid > 0) {
		pid_t  pid;
		cint_t tries = 2;

		while (true) {
			do {
				pid = waitpid (cgi->pid, NULL, WNOHANG);
			} while ((pid == 1) && (errno == EINTR));

			if (pid > 0) {
				/* Ok */
				break;
			} else if (errno == ECHILD) {
				/* Already death */
				break;
			}

			/* Failed */
			kill (cgi->pid, SIGTERM);

			tries--;
			if (tries < 0)
				break;
		}
	}
#else
	if (cgi->process) {
		WaitForSingleObject (cgi->process, INFINITE);
		CloseHandle (cgi->process);
	}

	if (cgi->thread) {
		CloseHandle (cgi->thread);
	}
#endif

        /* Free the environment variables
	 */
#ifdef _WIN32
	cherokee_buffer_mrproper (&cgi->envp);
#else
	for (i=0; i<cgi->envp_last; i++) {
		free (cgi->envp[i]);
		cgi->envp[i] = NULL;
	}
#endif

	/* For some reason, we have seen that the SIGCHLD signal does not call to
	 * our handler in a server with a lot of requests, so the wait() call,
	 * necessary to free the resources used by the CGI, is not called. So I
	 * think that a possible solution couble be to put the waitpid call in the
	 * _free method of this handler, so when the handler ends, this will free
	 * the resources used by our cool CGI.
	 */
	do_reap();

	return ret_ok;
}
Ejemplo n.º 14
0
static ret_t
fork_and_execute_cgi_via_spawner(cherokee_handler_cgi_t *cgi)
{
	int                          re;
	int                          pid           = -1;
	cherokee_connection_t       *conn          = HANDLER_CONN(cgi);
	cherokee_handler_cgi_base_t *cgi_base      = HDL_CGI_BASE(cgi);
	ret_t                        ret;
	uid_t                        uid;
	gid_t                        gid;
	cherokee_buffer_t            empty         = CHEROKEE_BUF_INIT;
	cherokee_buffer_t            username      = CHEROKEE_BUF_INIT;
	cherokee_services_fdmap_t    fd_map;
	cherokee_buffer_t           *chdir         = NULL;
	cherokee_buffer_t            chdir_backing = CHEROKEE_BUF_INIT;
	struct passwd                ent;
	char                         ent_tmp[1024];

	struct {
		int cgi[2];
		int server[2];
	} pipes;

	TRACE (ENTRIES, "Trying to create CGI via spawner\n");

	if (! cherokee_buffer_is_empty (&conn->effective_directory)) {
		chdir = &conn->effective_directory;
	} else {
		int ofs = cgi_base->executable.len - 1;
		while (ofs >= 0 && cgi_base->executable.buf[ofs] != '/') {
			ofs--;
		}
		TRACE (ENTRIES, "While building chdir, ofs=%d\n", ofs);
		if (ofs < 0 || cherokee_buffer_add (&chdir_backing,
						    cgi_base->executable.buf,
						    ofs + 1) != ret_ok) {
			conn->error_code = http_internal_error;
			TRACE (ENTRIES, "Failed, cannot build chdir entry\n");
			cherokee_buffer_mrproper(&chdir_backing);
			return ret_error;
		}
		chdir = &chdir_backing;
	}

	/* Creates the pipes ...
	 */
	re  = cherokee_pipe (pipes.cgi);
	re |= cherokee_pipe (pipes.server);

	if (re != 0) {
		conn->error_code = http_internal_error;
		cherokee_buffer_mrproper(&chdir_backing);
		TRACE (ENTRIES, "Failed, cannot build pipes\n");
		return ret_error;
	}

	if (HANDLER_CGI_PROPS(cgi_base)->change_user) {
		struct stat                        nocache_info;
		struct stat                       *info;
		cherokee_iocache_entry_t          *io_entry = NULL;
		cherokee_server_t                 *srv      = CONN_SRV(conn);
		cherokee_handler_cgi_base_props_t *props    = HANDLER_CGI_BASE_PROPS(cgi);

		ret = cherokee_io_stat (srv->iocache, &cgi_base->executable, props->use_cache, &nocache_info, &io_entry, &info);
		if (ret != ret_ok) {
			info = &nocache_info;
			nocache_info.st_uid = getuid();
			nocache_info.st_gid = getgid();
		}

		uid = info->st_uid;
		gid = info->st_gid;

		cherokee_iocache_entry_unref(&io_entry);
	} else {
		/* Not changing, so launch as the same uid/gid as the worker */
		uid = getuid();
		gid = getgid();
	}

	/* Determine the username of the owner of the file */
	ret = cherokee_getpwuid(uid, &ent, ent_tmp, sizeof (ent_tmp));
	if (ret != ret_ok ||
	    cherokee_buffer_add(&username,
				ent.pw_name,
				strlen(ent.pw_name) != ret_ok)) {
		cherokee_fd_close(pipes.cgi[0]);
		cherokee_fd_close(pipes.cgi[1]);
		cherokee_fd_close(pipes.server[0]);
		cherokee_fd_close(pipes.server[1]);
		conn->error_code = http_internal_error;
		cherokee_buffer_mrproper(&chdir_backing);
		cherokee_buffer_mrproper(&username);
		TRACE (ENTRIES, "Failed, Unable to retrieve username for uid %d\n", uid);
		return ret_error;
	}


	/* Update the environment ready to run */
	add_environment (cgi, conn);

	/* Set up the FD map */
	fd_map.fd_in = pipes.server[0];
	fd_map.fd_out = pipes.cgi[1];

	if ((CONN_VSRV(conn)->error_writer != NULL) &&
	    (CONN_VSRV(conn)->error_writer->fd != -1)) {
		fd_map.fd_err = CONN_VSRV(conn)->error_writer->fd;
	} else {
		fd_map.fd_err = fd_map.fd_out;
	}


	TRACE (ENTRIES, "Doing Spawn\n");
	ret = cherokee_services_client_spawn (&cgi_base->executable,
					      &username,
					      uid,
					      gid,
					      &empty,
					      chdir,
					      false,
					      cgi->envp,
					      CONN_VSRV(conn)->error_writer,
					      &pid,
					      &fd_map);

	cherokee_buffer_mrproper(&chdir_backing);
	cherokee_buffer_mrproper(&username);

	/* Close the client FDs */
	cherokee_fd_close (pipes.server[0]);
	cherokee_fd_close (pipes.cgi[1]);

	/* Did we fail to try to spawn? */
	if (ret != ret_ok) {
		/* Close the server FDs too */
		cherokee_fd_close (pipes.server[1]);
		cherokee_fd_close (pipes.cgi[0]);
		TRACE (ENTRIES, "Failed to spawn\n");
		return ret;
	}

	/* Did we try, but fail, to spawn? */
	if (pid == -1) {
		/* Close the server FDs too */
		cherokee_fd_close (pipes.server[1]);
		cherokee_fd_close (pipes.cgi[0]);
		TRACE (ENTRIES, "Spawned, but failed server side\n");
		return ret_error;
	}

	/* Successfully launched */
	cgi->pid        = pid;
	cgi->pipeInput  = pipes.cgi[0];
	cgi->pipeOutput = pipes.server[1];

	/* Set to Input to NON-BLOCKING
	 */
	_fd_set_properties (cgi->pipeInput, O_NDELAY|O_NONBLOCK, 0);

	TRACE (ENTRIES, "CGI running, PID=%d\n", pid);
	return ret_ok;
}
Ejemplo n.º 15
0
static NORETURN void
manage_child_cgi_process (cherokee_handler_cgi_t *cgi, int pipe_cgi[2], int pipe_server[2])
{
	/* Child process
	 */
	int                          re;
	cherokee_connection_t       *conn          = HANDLER_CONN(cgi);
	cherokee_handler_cgi_base_t *cgi_base      = HDL_CGI_BASE(cgi);
	char                        *absolute_path = cgi_base->executable.buf;
	char                        *argv[2]       = { NULL, NULL };

#ifdef TRACE_ENABLED
	TRACE(ENTRIES, "About to execute: '%s'\n", absolute_path);

	if (! cherokee_buffer_is_empty (&conn->effective_directory))
		TRACE(ENTRIES, "Effective directory: '%s'\n", conn->effective_directory.buf);
	else
		TRACE(ENTRIES, "No Effective directory %s", "\n");
#endif

	/* Close useless sides
	 */
	cherokee_fd_close (pipe_cgi[0]);
	cherokee_fd_close (pipe_server[1]);

	/* Change stdin and out
	 */
	cherokee_fd_close (STDIN_FILENO);
	re = dup2 (pipe_server[0], STDIN_FILENO);
	cherokee_fd_close (pipe_server[0]);

	if (unlikely (re != 0)) {
		printf ("Status: 500" CRLF_CRLF);
		printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__);
		exit(1);
	}

	cherokee_fd_close (STDOUT_FILENO);
	re |= dup2 (pipe_cgi[1], STDOUT_FILENO);
	cherokee_fd_close (pipe_cgi[1]);

	/* Redirect the stderr
	 */
	if ((CONN_VSRV(conn)->error_writer != NULL) &&
	    (CONN_VSRV(conn)->error_writer->fd != -1))
	{
		cherokee_fd_close (STDERR_FILENO);
		dup2 (CONN_VSRV(conn)->error_writer->fd, STDERR_FILENO);
	}

# if 0
	/* Set unbuffered
	 */
	setvbuf (stdin,  NULL, _IONBF, 0);
	setvbuf (stdout, NULL, _IONBF, 0);
# endif

	/* Enable blocking mode
	 */
	_fd_set_properties (STDIN_FILENO,  0, O_NONBLOCK);
	_fd_set_properties (STDOUT_FILENO, 0, O_NONBLOCK);
	_fd_set_properties (STDERR_FILENO, 0, O_NONBLOCK);

	/* Sets the new environ.
	 */
	add_environment (cgi, conn);

	/* Change the directory
	 */
	if (! cherokee_buffer_is_empty (&conn->effective_directory)) {
		re = chdir (conn->effective_directory.buf);
	} else {
		char *file = strrchr (absolute_path, '/');

		if (file != NULL) {
			*file = '\0';
			re = chdir (absolute_path);
			*file = '/';
		} else {
			re = -1;
		}
	}

	if (re < 0) {
		printf ("Status: 500" CRLF_CRLF);
		printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__);
		exit(1);
	}

	/* Build de argv array
	 */
	argv[0] = absolute_path;

	/* Change the execution user?
	 */
	if (HANDLER_CGI_PROPS(cgi_base)->change_user) {
		struct stat                        nocache_info;
		struct stat                       *info;
		cherokee_iocache_entry_t          *io_entry = NULL;
		cherokee_server_t                 *srv      = CONN_SRV(conn);
		cherokee_handler_cgi_base_props_t *props    = HANDLER_CGI_BASE_PROPS(cgi);

		ret_t ret = cherokee_io_stat (srv->iocache, &cgi_base->executable, props->use_cache, &nocache_info, &io_entry, &info);
		if (ret != ret_ok) {
			info = &nocache_info;
		}

		re = setuid (info->st_uid);
		if (re != 0) {
			LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_SETID, absolute_path, info->st_uid);
		}

		cherokee_iocache_entry_unref(&io_entry);
	}

	/* Reset the server-wide signal handlers
	 */
	cherokee_reset_signals();

	/* Lets go.. execute it!
	 */
	do {
		re = execve (absolute_path, argv, cgi->envp);
	} while ((re == -1) && (errno == EINTR));

	if (re < 0) {
		int err = errno;
		char buferr[ERROR_MAX_BUFSIZE];

		switch (err) {
		case ENODEV:
		case ENOTDIR:
		case ENOENT:
			printf ("Status: 404" CRLF_CRLF);
			exit(0);
		case EPERM:
		case EACCES:
		case ENOEXEC:
			printf ("Status: 403" CRLF_CRLF);
			exit(0);
		default:
			printf ("Status: 500" CRLF_CRLF);
			printf ("X-Debug: file=%s line=%d cmd=%s errno=%d: %s" CRLF_CRLF,
			        __FILE__, __LINE__, absolute_path, err, strerror(err));
		}

		/* Don't use the logging system (concurrency issues)
		 */
		LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_EXECUTE,
		           absolute_path, cherokee_strerror_r(err, buferr, sizeof(buferr)));
		exit(1);
	}

	/* There is no way, it could reach this point.
	 */
	SHOULDNT_HAPPEN;
	exit(2);
}