Example #1
0
	WinPortFSNotify(LPCWSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter)
		: WinPortEvent(true, false), _watcher(0),
		_fd(-1), _filter(dwNotifyFilter), _watching(false)
	{
#if defined(__APPLE__) || defined(__FreeBSD__)
		_fd = kqueue();
		if (_fd == -1)
			return;
#else
		_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
		if (_fd == -1)
			return;
#endif

		AddWatch( Wide2MB(lpPathName).c_str() );
		if (bWatchSubtree) {
			AddWatchRecursive(Wide2MB(lpPathName), 0);
		}

		if (!_watches.empty() && pipe_cloexec(_pipe)==0) {
#if defined(__APPLE__) || defined(__FreeBSD__)
			_events.emplace_back();
			EV_SET(&_events.back(), _pipe[0], EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0);
#endif
			_watching = true;
			if (pthread_create(&_watcher, nullptr, sWatcherProc, this)==0) {
				fprintf(stderr, "WinPortFSNotify('%ls') - watching\n", lpPathName);
			} else {
				fprintf(stderr, "WinPortFSNotify('%ls') - pthread error %u\n", lpPathName, errno);
				close(_pipe[0]);
				close(_pipe[1]);
				_watching = false;				
			}
		} else {
			fprintf(stderr, "WinPortFSNotify('%ls') - not watching\n", lpPathName);
		}
	}
Example #2
0
void PerfSource::run() {
	int pipefd[2];
	pthread_t procThread;
	ProcThreadArgs procThreadArgs;

	{
		DynBuf printb;
		DynBuf b1;
		DynBuf b2;

		const uint64_t currTime = getTime();

		// Start events before reading proc to avoid race conditions
		if (!mCountersGroup.start() || !mIdleGroup.start()) {
			logg->logError(__FILE__, __LINE__, "PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__);
			handleException();
		}

		if (!readProcComms(currTime, &mBuffer, &printb, &b1, &b2)) {
			logg->logError(__FILE__, __LINE__, "readProcComms failed");
			handleException();
		}
		mBuffer.commit(currTime);

		// Postpone reading kallsyms as on android adb gets too backed up and data is lost
		procThreadArgs.mBuffer = &mBuffer;
		procThreadArgs.mCurrTime = currTime;
		procThreadArgs.mIsDone = false;
		if (pthread_create(&procThread, NULL, procFunc, &procThreadArgs)) {
			logg->logError(__FILE__, __LINE__, "pthread_create failed", __FUNCTION__, __FILE__, __LINE__);
			handleException();
		}
	}

	if (pipe_cloexec(pipefd) != 0) {
		logg->logError(__FILE__, __LINE__, "pipe failed");
		handleException();
	}
	mInterruptFd = pipefd[1];

	if (!mMonitor.add(pipefd[0])) {
		logg->logError(__FILE__, __LINE__, "Monitor::add failed");
		handleException();
	}

	int timeout = -1;
	if (gSessionData->mLiveRate > 0) {
		timeout = gSessionData->mLiveRate/NS_PER_MS;
	}

	sem_post(mStartProfile);

	while (gSessionData->mSessionIsActive) {
		// +1 for uevents, +1 for pipe
		struct epoll_event events[NR_CPUS + 2];
		int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout);
		if (ready < 0) {
			logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
			handleException();
		}
		const uint64_t currTime = getTime();

		for (int i = 0; i < ready; ++i) {
			if (events[i].data.fd == mUEvent.getFd()) {
				if (!handleUEvent(currTime)) {
					logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed");
					handleException();
				}
				break;
			}
		}

		// send a notification that data is ready
		sem_post(mSenderSem);

		// In one shot mode, stop collection once all the buffers are filled
		// Assume timeout == 0 in this case
		if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
			logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__);
			child->endSession();
		}
	}

	procThreadArgs.mIsDone = true;
	pthread_join(procThread, NULL);
	mIdleGroup.stop();
	mCountersGroup.stop();
	mBuffer.setDone();
	mIsDone = true;

	// send a notification that data is ready
	sem_post(mSenderSem);

	mInterruptFd = -1;
	close(pipefd[0]);
	close(pipefd[1]);
}
// Gator data flow: collector -> collector fifo -> sender
int main(int argc, char** argv) {
	// Ensure proper signal handling by making gatord the process group leader
	//   e.g. it may not be the group leader when launched as 'sudo gatord'
	setsid();

  // Set up global thread-safe logging
	logg = new Logging(hasDebugFlag(argc, argv));
	// Global data class
	gSessionData = new SessionData();
	// Set up global utility class
	util = new OlyUtility();

	// Initialize drivers
	new CCNDriver();

	prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
	pthread_mutex_init(&numSessions_mutex, NULL);

	signal(SIGINT, handler);
	signal(SIGTERM, handler);
	signal(SIGABRT, handler);

	// Set to high priority
	if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
		logg->logMessage("setpriority() failed");
	}

	// Parse the command line parameters
	struct cmdline_t cmdline = parseCommandLine(argc, argv);

	if (cmdline.update) {
		update(argv[0]);
		cmdline.update = false;
		gSessionData->mAllowCommands = true;
	}

	// Verify root permissions
	uid_t euid = geteuid();
	if (euid) {
		logg->logError("gatord must be launched with root privileges");
		handleException();
	}

	// Call before setting up the SIGCHLD handler, as system() spawns child processes
	if (!setupFilesystem(cmdline.module)) {
		logg->logMessage("Unable to set up gatorfs, trying perf");
		if (!gSessionData->perf.setup()) {
			logg->logError(
				       "Unable to locate gator.ko driver:\n"
				       "  >>> gator.ko should be co-located with gatord in the same directory\n"
				       "  >>> OR insmod gator.ko prior to launching gatord\n"
				       "  >>> OR specify the location of gator.ko on the command line\n"
				       "  >>> OR run Linux 3.4 or later with perf (CONFIG_PERF_EVENTS and CONFIG_HW_PERF_EVENTS) and tracing (CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER) support to collect data via userspace only");
			handleException();
		}
	}

	{
		EventsXML eventsXML;
		mxml_node_t *xml = eventsXML.getTree();
		// Initialize all drivers
		for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
			driver->readEvents(xml);
		}
		mxmlDelete(xml);
	}

	// Handle child exit codes
	signal(SIGCHLD, child_exit);

	// Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
	// Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
	signal(SIGPIPE, SIG_IGN);

	// If the command line argument is a session xml file, no need to open a socket
	if (gSessionData->mSessionXMLPath) {
		child = new Child();
		child->run();
		delete child;
	} else {
		annotateListener.setup();
		int pipefd[2];
		if (pipe_cloexec(pipefd) != 0) {
			logg->logError("Unable to set up annotate pipe");
			handleException();
		}
		gSessionData->mAnnotateStart = pipefd[1];
		sock = new OlyServerSocket(cmdline.port);
		udpListener.setup(cmdline.port);
		if (!monitor.init() ||
				!monitor.add(sock->getFd()) ||
				!monitor.add(udpListener.getReq()) ||
				!monitor.add(annotateListener.getSockFd()) ||
				!monitor.add(annotateListener.getUdsFd()) ||
				!monitor.add(pipefd[0]) ||
				false) {
			logg->logError("Monitor setup failed");
			handleException();
		}
		// Forever loop, can be exited via a signal or exception
		while (1) {
			struct epoll_event events[2];
			logg->logMessage("Waiting on connection...");
			int ready = monitor.wait(events, ARRAY_LENGTH(events), -1);
			if (ready < 0) {
				logg->logError("Monitor::wait failed");
				handleException();
			}
			for (int i = 0; i < ready; ++i) {
				if (events[i].data.fd == sock->getFd()) {
					handleClient();
				} else if (events[i].data.fd == udpListener.getReq()) {
					udpListener.handle();
				} else if (events[i].data.fd == annotateListener.getSockFd()) {
					annotateListener.handleSock();
				} else if (events[i].data.fd == annotateListener.getUdsFd()) {
					annotateListener.handleUds();
				} else if (events[i].data.fd == pipefd[0]) {
					uint64_t val;
					if (read(pipefd[0], &val, sizeof(val)) != sizeof(val)) {
						logg->logMessage("Reading annotate pipe failed");
					}
					annotateListener.signal();
				}
			}
		}
	}

	cleanUp();
	return 0;
}
Example #4
0
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) {
	pid_t pid;

	int to_cgi_fds[2];
	int from_cgi_fds[2];
	struct stat st;
	UNUSED(p);

#ifndef __WIN32

	if (!buffer_string_is_empty(cgi_handler)) {
		/* stat the exec file */
		if (-1 == (stat(cgi_handler->ptr, &st))) {
			log_error_write(srv, __FILE__, __LINE__, "sbss",
					"stat for cgi-handler", cgi_handler,
					"failed:", strerror(errno));
			return -1;
		}
	}

	if (pipe_cloexec(to_cgi_fds)) {
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}

	if (pipe_cloexec(from_cgi_fds)) {
		close(to_cgi_fds[0]);
		close(to_cgi_fds[1]);
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}

	/* fork, execve */
	switch (pid = fork()) {
	case 0: {
		/* child */
		char **args;
		int argc;
		int i = 0;
		char_array env;
		char *c;
		const char *s;
		http_cgi_opts opts = { 0, 0, NULL, NULL };

		/* move stdout to from_cgi_fd[1] */
		dup2(from_cgi_fds[1], STDOUT_FILENO);
	      #ifndef FD_CLOEXEC
		close(from_cgi_fds[1]);
		/* not needed */
		close(from_cgi_fds[0]);
	      #endif

		/* move the stdin to to_cgi_fd[0] */
		dup2(to_cgi_fds[0], STDIN_FILENO);
	      #ifndef FD_CLOEXEC
		close(to_cgi_fds[0]);
		/* not needed */
		close(to_cgi_fds[1]);
	      #endif

		/* create environment */
		env.ptr = NULL;
		env.size = 0;
		env.used = 0;

		http_cgi_headers(srv, con, &opts, cgi_env_add, &env);

		/* for valgrind */
		if (NULL != (s = getenv("LD_PRELOAD"))) {
			cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
		}

		if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
			cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
		}
#ifdef __CYGWIN__
		/* CYGWIN needs SYSTEMROOT */
		if (NULL != (s = getenv("SYSTEMROOT"))) {
			cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
		}
#endif

		if (env.size == env.used) {
			env.size += 16;
			env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
		}

		env.ptr[env.used] = NULL;

		/* set up args */
		argc = 3;
		args = malloc(sizeof(*args) * argc);
		force_assert(args);
		i = 0;

		if (!buffer_string_is_empty(cgi_handler)) {
			args[i++] = cgi_handler->ptr;
		}
		args[i++] = con->physical.path->ptr;
		args[i  ] = NULL;

		/* search for the last / */
		if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
			/* handle special case of file in root directory */
			const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr;

			/* temporarily shorten con->physical.path to directory without terminating '/' */
			*c = '\0';
			/* change to the physical directory */
			if (-1 == chdir(physdir)) {
				log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
			}
			*c = '/';
		}

		/* we don't need the client socket */
		for (i = 3; i < 256; i++) {
			if (i != srv->errorlog_fd) close(i);
		}

		/* exec the cgi */
		execve(args[0], args, env.ptr);

		/* most log files may have been closed/redirected by this point,
		 * though stderr might still point to lighttpd.breakage.log */
		perror(args[0]);
		_exit(1);
	}
	case -1:
		/* error */
		log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
		close(from_cgi_fds[0]);
		close(from_cgi_fds[1]);
		close(to_cgi_fds[0]);
		close(to_cgi_fds[1]);
		return -1;
	default: {
		/* parent process */

		close(from_cgi_fds[1]);
		close(to_cgi_fds[0]);

		/* register PID and wait for them asynchronously */

		hctx->pid = pid;
		hctx->fd = from_cgi_fds[0];
		hctx->fde_ndx = -1;

		++srv->cur_fds;

		if (0 == con->request.content_length) {
			close(to_cgi_fds[1]);
		} else {
			/* there is content to send */
			if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
				close(to_cgi_fds[1]);
				cgi_connection_close(srv, hctx);
				return -1;
			}

			if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) {
				close(to_cgi_fds[1]);
				cgi_connection_close(srv, hctx);
				return -1;
			}

			++srv->cur_fds;
		}

		fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
		if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) {
			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
			cgi_connection_close(srv, hctx);
			return -1;
		}
		fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);

		break;
	}
	}

	return 0;
#else
	return -1;
#endif
}
void *commandThread(void *) {
	prctl(PR_SET_NAME, (unsigned long)&"gatord-command", 0, 0, 0);

	const char *const name = gSessionData->mCaptureUser == NULL ? "nobody" : gSessionData->mCaptureUser;
	const int uid = getUid(name);
	if (uid < 0) {
		logg->logError("Unable to look up the user %s, please double check that the user exists", name);
		handleException();
	}

	sleep(3);

	char buf[128];
	int pipefd[2];
	if (pipe_cloexec(pipefd) != 0) {
		logg->logError("pipe failed");
		handleException();
	}

	const int pid = fork();
	if (pid < 0) {
		logg->logError("fork failed");
		handleException();
	}
	if (pid == 0) {
		char cargv0l[] = "/bin/sh";
		char cargv0a[] = "/system/bin/sh";
		char cargv1[] = "-c";
		char *cargv[] = {
			cargv0l,
			cargv1,
			gSessionData->mCaptureCommand,
			NULL,
		};

		buf[0] = '\0';
		close(pipefd[0]);

		// Gator runs at a high priority, reset the priority to the default
		if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), 0) == -1) {
			snprintf(buf, sizeof(buf), "setpriority failed");
			goto fail_exit;
		}

		if (setuid(uid) != 0) {
			snprintf(buf, sizeof(buf), "setuid failed");
			goto fail_exit;
		}

		{
			const char *const path = gSessionData->mCaptureWorkingDir == NULL ? "/" : gSessionData->mCaptureWorkingDir;
			if (chdir(path) != 0) {
				snprintf(buf, sizeof(buf), "Unable to cd to %s, please verify the directory exists and is accessable to %s", path, name);
				goto fail_exit;
			}
		}

		execv(cargv[0], cargv);
		cargv[0] = cargv0a;
		execv(cargv[0], cargv);
		snprintf(buf, sizeof(buf), "execv failed");

	fail_exit:
		if (buf[0] != '\0') {
			const ssize_t bytes = write(pipefd[1], buf, sizeof(buf));
			// Can't do anything if this fails
			(void)bytes;
		}

		exit(-1);
	}

	close(pipefd[1]);
	const ssize_t bytes = read(pipefd[0], buf, sizeof(buf));
	if (bytes > 0) {
		logg->logError("%s", buf);
		handleException();
	}
	close(pipefd[0]);

	return NULL;
}
void ExternalSource::run() {
	int pipefd[2];

	prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0);

	if (pipe_cloexec(pipefd) != 0) {
		logg->logError(__FILE__, __LINE__, "pipe failed");
		handleException();
	}
	mInterruptFd = pipefd[1];

	if (!mMonitor.add(pipefd[0])) {
		logg->logError(__FILE__, __LINE__, "Monitor::add failed");
		handleException();
	}

	// Notify annotate clients to retry connecting to gatord
	gSessionData->annotateListener.signal();

	while (gSessionData->mSessionIsActive) {
		struct epoll_event events[16];
		// Clear any pending sem posts
		while (sem_trywait(&mBufferSem) == 0);
		int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1);
		if (ready < 0) {
			logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
			handleException();
		}

		const uint64_t currTime = getTime();

		for (int i = 0; i < ready; ++i) {
			const int fd = events[i].data.fd;
			if (fd == mMveStartupUds.getFd()) {
				// Mali Video Engine says it's alive
				int client = mMveStartupUds.acceptConnection();
				// Don't read from this connection, establish a new connection to Mali-V500
				close(client);
				if (!connectMve()) {
					logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection");
					handleException();
				}
			} else if (fd == mMaliStartupUds.getFd()) {
				// Mali Graphics says it's alive
				int client = mMaliStartupUds.acceptConnection();
				// Don't read from this connection, establish a new connection to Mali Graphics
				close(client);
				if (!connectMali()) {
					logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali graphics connection");
					handleException();
				}
			} else if (fd == mAnnotate.getFd()) {
				int client = mAnnotate.acceptConnection();
				if (!setNonblock(client) || !mMonitor.add(client)) {
					logg->logError(__FILE__, __LINE__, "Unable to set socket options on incoming annotation connection");
					handleException();
				}
			} else if (fd == pipefd[0]) {
				// Means interrupt has been called and mSessionIsActive should be reread
			} else {
				/* This can result in some starvation if there are multiple
				 * threads which are annotating heavily, but it is not
				 * recommended that threads annotate that much as it can also
				 * starve out the gator data.
				 */
				while (gSessionData->mSessionIsActive) {
					// Wait until there is enough room for the fd, two headers and two ints
					waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t));
					mBuffer.packInt(fd);
					const int contiguous = mBuffer.contiguousSpaceAvailable();
					const int bytes = read(fd, mBuffer.getWritePos(), contiguous);
					if (bytes < 0) {
						if (errno == EAGAIN) {
							// Nothing left to read
							mBuffer.commit(currTime);
							break;
						}
						// Something else failed, close the socket
						mBuffer.commit(currTime);
						mBuffer.packInt(-1);
						mBuffer.packInt(fd);
						mBuffer.commit(currTime);
						close(fd);
						break;
					} else if (bytes == 0) {
						// The other side is closed
						mBuffer.commit(currTime);
						mBuffer.packInt(-1);
						mBuffer.packInt(fd);
						mBuffer.commit(currTime);
						close(fd);
						break;
					}

					mBuffer.advanceWrite(bytes);
					mBuffer.commit(currTime);

					// Short reads also mean nothing is left to read
					if (bytes < contiguous) {
						break;
					}
				}
			}
		}
	}

	mBuffer.setDone();

	if (mMveUds >= 0) {
		gSessionData->maliVideo.stop(mMveUds);
	}

	mInterruptFd = -1;
	close(pipefd[0]);
	close(pipefd[1]);
}
Example #7
0
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) {
	char *args[3];
	int to_cgi_fds[2];
	int from_cgi_fds[2];
	int dfd = -1;
	UNUSED(p);

	if (!buffer_string_is_empty(cgi_handler)) {
		if (NULL == cgi_stat(srv, con, cgi_handler)) {
			log_error_write(srv, __FILE__, __LINE__, "sbss",
					"stat for cgi-handler", cgi_handler,
					"failed:", strerror(errno));
			return -1;
		}
	}

	if (pipe_cloexec(to_cgi_fds)) {
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}
	if (pipe_cloexec(from_cgi_fds)) {
		close(to_cgi_fds[0]);
		close(to_cgi_fds[1]);
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}
	fdevent_setfd_cloexec(to_cgi_fds[1]);
	fdevent_setfd_cloexec(from_cgi_fds[0]);

	{
		size_t i = 0;
		http_cgi_opts opts = { 0, 0, NULL, NULL };
		env_accum *env = &p->env;
		env->used = 0;
		env->oused = 0;

		/* create environment */

		http_cgi_headers(srv, con, &opts, cgi_env_add, env);

		/* for valgrind */
		if (p->env.ld_preload) {
			cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload));
		}
		if (p->env.ld_library_path) {
			cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path));
		}
	      #ifdef __CYGWIN__
		/* CYGWIN needs SYSTEMROOT */
		if (p->env.systemroot) {
			cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot));
		}
	      #endif

		if (env->esize <= env->oused) {
			env->esize = (env->oused + 1 + 0xf) & ~(0xfuL);
			env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr));
			force_assert(env->eptr);
		}
		for (i = 0; i < env->oused; ++i) {
			env->eptr[i] = env->ptr + env->offsets[i];
		}
		env->eptr[env->oused] = NULL;

		/* set up args */
		i = 0;

		if (!buffer_string_is_empty(cgi_handler)) {
			args[i++] = cgi_handler->ptr;
		}
		args[i++] = con->physical.path->ptr;
		args[i  ] = NULL;
	}

	dfd = fdevent_open_dirname(con->physical.path->ptr, con->conf.follow_symlink);
	if (-1 == dfd) {
		log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path);
	}

	hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], -1, dfd) : -1;

	if (-1 == hctx->pid) {
		/* log error with errno prior to calling close() (might change errno) */
		log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
		if (-1 != dfd) close(dfd);
		close(from_cgi_fds[0]);
		close(from_cgi_fds[1]);
		close(to_cgi_fds[0]);
		close(to_cgi_fds[1]);
		return -1;
	} else {
		if (-1 != dfd) close(dfd);
		close(from_cgi_fds[1]);
		close(to_cgi_fds[0]);

		hctx->fd = from_cgi_fds[0];

		++srv->cur_fds;

		cgi_pid_add(p, hctx->pid, hctx);

		if (0 == con->request.content_length) {
			close(to_cgi_fds[1]);
		} else {
			/* there is content to send */
			if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
				close(to_cgi_fds[1]);
				cgi_connection_close(srv, hctx);
				return -1;
			}

			if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) {
				close(to_cgi_fds[1]);
				cgi_connection_close(srv, hctx);
				return -1;
			}

			++srv->cur_fds;
		}

		hctx->fdn = fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
		if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) {
			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
			cgi_connection_close(srv, hctx);
			return -1;
		}
		fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP);

		return 0;
	}
}