aboutsummaryrefslogblamecommitdiffstats
path: root/libcoopgamma_query__.c
blob: 706a3cfa7a9101cc1ad52f29503cdb0b173d2a9c (plain) (tree)






















































































































                                                                                                      
/* See LICENSE file for copyright and license details. */
#include "common.h"


/**
 * Run coopgammad with -q or -qq and return the response
 * 
 * SIGCHLD must not be ignored or blocked
 * 
 * @param   method   The adjustment method, `NULL` for automatic
 * @param   site     The site, `NULL` for automatic
 * @param   arg      "-q" or "-qq", which shall be passed to coopgammad?
 * @return           The output of coopgammad, `NULL` on error. This
 *                   will be NUL-terminated and will not contain any
 *                   other `NUL` bytes.
 */
char *
libcoopgamma_query__(const char *restrict method, const char *restrict site, const char *restrict arg)
{
	const char *(args[7]) = {COOPGAMMAD, arg};
	size_t i = 2, n = 0, size = 0;
	int pipe_rw[2] = { -1, -1 };
	pid_t pid;
	int saved_errno, status;
	char *msg = NULL;
	ssize_t got;
	void *new;

	if (method) args[i++] = "-m", args[i++] = method;
	if (site)   args[i++] = "-s", args[i++] = site;
	args[i] = NULL;

	if (pipe(pipe_rw) < 0)
		goto fail;

	switch ((pid = fork())) {
	case -1:
		goto fail;

	case 0:
		/* Child */
		close(pipe_rw[0]);
		if (pipe_rw[1] != STDOUT_FILENO) {
			close(STDOUT_FILENO);
			if (dup2(pipe_rw[1], STDOUT_FILENO) < 0)
				goto fail_child;
			close(pipe_rw[1]);
		}
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-qual"
#endif
		execvp(COOPGAMMAD, (char* const*)(args));
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
	fail_child:
		saved_errno = errno;
		perror(NAME_OF_THE_PROCESS);
		if (write(STDOUT_FILENO, &saved_errno, sizeof(int)) != sizeof(int))
			perror(NAME_OF_THE_PROCESS);
		exit(1);

	default:
		/* Parent */
		close(pipe_rw[1]), pipe_rw[1] = -1;
		for (;;) {
			if (n == size) {
				new = realloc(msg, size = (n ? (n << 1) : 256U));
				if (!new)
					goto fail;
				msg = new;
			}
			got = read(pipe_rw[0], &msg[n], size - n);
			if (got < 0) {
				if (errno == EINTR)
					continue;
				goto fail;
			} else if (got == 0) {
				break;
			}
			n += (size_t)got;
		}
		close(pipe_rw[0]), pipe_rw[0] = -1;
		if (waitpid(pid, &status, 0) < 0)
			goto fail;
		if (status) {
			errno = EINVAL;
			if (n == sizeof(int) && *(int *)msg)
				errno = *(int*)msg;
		}
		break;
	}

	if (n == size) {
		new = realloc(msg, n + 1U);
		if (!new)
			goto fail;
		msg = new;
	}
	msg[n] = '\0';

	if (strchr(msg, '\0') != msg + n) {
		errno = EBADMSG;
		goto fail;
	}

	return msg;

fail:
	saved_errno = errno;
	if (pipe_rw[0] >= 0)
		close(pipe_rw[0]);
	if (pipe_rw[1] >= 0)
		close(pipe_rw[1]);
	free(msg);
	errno = saved_errno;
	return NULL;
}