aboutsummaryrefslogblamecommitdiffstats
path: root/libcoopgamma_connect.c
blob: d6c931c27cb78e4b0a9b5b9d5864fb50611e7249 (plain) (tree)





























































































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


/**
 * Connect to a coopgamma server, and start it if necessary
 * 
 * Use `libcoopgamma_context_destroy` to disconnect
 * 
 * SIGCHLD must not be ignored or blocked
 * 
 * @param   method  The adjustment method, `NULL` for automatic
 * @param   site    The site, `NULL` for automatic
 * @param   ctx     The state of the library, must be initialised
 * @return          Zero on success, -1 on error. On error, `errno` is set
 *                  to 0 if the server could not be initialised.
 */
int
libcoopgamma_connect(const char *restrict method, const char *restrict site, libcoopgamma_context_t *restrict ctx)
{
	const char *(args[6]) = {COOPGAMMAD};
	struct sockaddr_un address;
	char *path;
	int saved_errno;
	int tries = 0, status;
	pid_t pid;
	size_t i = 1;

	ctx->blocking = 1;

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

	path = libcoopgamma_get_socket_file(method, site);
	if (!path)
		return -1;
	if (strlen(path) >= sizeof(address.sun_path)) {
		free(path);
		errno = ENAMETOOLONG;
		return -1;
	}

	address.sun_family = AF_UNIX;
	stpcpy(address.sun_path, path);
	free(path);
	if ((ctx->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
		return -1;

retry:
	if (connect(ctx->fd, (const struct sockaddr *)&address, (socklen_t)sizeof(address)) < 0) {
		if ((errno == ECONNREFUSED || errno == ENOENT || errno == ENOTDIR) && !tries++) {
			switch ((pid = fork())) {
			case -1:
				goto fail;

			case 0:
				/* Child */
				close(ctx->fd);
#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
				perror(NAME_OF_THE_PROCESS);
				exit(1);

			default:
				/* Parent */
				if (waitpid(pid, &status, 0) < 0)
					goto fail;
				if (status) {
					errno = 0;
					goto fail;
				}
				break;
			}
			goto retry;
		}
		goto fail;
	}

	return 0;

fail:
	saved_errno = errno;
	close(ctx->fd);
	ctx->fd = -1;
	errno = saved_errno;
	return -1;
}