aboutsummaryrefslogtreecommitdiffstats
path: root/libcoopgamma_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcoopgamma_connect.c')
-rw-r--r--libcoopgamma_connect.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/libcoopgamma_connect.c b/libcoopgamma_connect.c
new file mode 100644
index 0000000..d6c931c
--- /dev/null
+++ b/libcoopgamma_connect.c
@@ -0,0 +1,94 @@
+/* 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;
+}