--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "srvctrl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/fcntl.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <ucx/utils.h>
+#include <libidav/utils.h>
+
+#define TIMEOUT_IDLE -1
+#define TIMEOUT_CLIENT 1000
+#define CLIENT_UPDATE_INTERVALL 1
+
+static char *socket_path;
+
+static int srvctrl;
+
+
+int create_control_socket(void) {
+ const char *copydir = mzcp_get_copydir();
+
+ // create unix domain socket
+ char *random_str = util_random_str();
+ sstr_t socketp = ucx_sprintf("%s/%.*s", copydir, 8, random_str);
+ free(random_str);
+ socket_path = socketp.ptr;
+
+ struct sockaddr_un addr;
+ if(socketp.length > sizeof(addr.sun_path)-1) {
+ fprintf(stderr,
+ "path '%s' too long for unix domain socket",
+ socketp.ptr);
+ return 1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, socketp.ptr, socketp.length);
+
+ srvctrl = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(srvctrl == -1) {
+ fprintf(stderr,
+ "Cannot create server control socket: %s",
+ strerror(errno));
+ return 1;
+ }
+ if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) {
+ fprintf(stderr,
+ "srvctrl socket bind failed: %s",
+ strerror(errno));
+ return 1;
+ }
+
+ listen(srvctrl, 4);
+
+ return 0;
+}
+
+const char* mzcp_get_socketpath(void) {
+ return socket_path;
+}
+
+int mzcp_srvctrl(CPSettings *settings) {
+
+ size_t allocfds = 8;
+ size_t numfds = 1;
+
+ struct pollfd *fds = calloc(allocfds, sizeof(struct pollfd));
+ CtrlClient **clients = calloc(allocfds, sizeof(void*));
+
+ int timeout = TIMEOUT_IDLE;
+
+ fds[0].fd = srvctrl;
+ fds[0].events = POLLIN;
+
+ int abort = 0;
+
+ time_t tbegin = time(NULL);
+
+ while(poll(fds, numfds, 1000) >= 0) {
+ time_t tend = time(NULL);
+ time_t diff = tend - tbegin;
+ tbegin = tend;
+
+ if((fds[0].revents & POLLIN) == POLLIN) {
+ printf("accept\n");
+ int fd = accept(srvctrl, NULL, 0);
+ if(fd < 0) {
+ break;
+ }
+
+ //int flags = fcntl(fd, F_GETFL, 0);
+ //flags = flags & ~O_NONBLOCK;
+ //fcntl(fd, F_SETFL, flags);
+
+ CtrlClient *client = malloc(sizeof(CtrlClient));
+ memset(client, 0, sizeof(CtrlClient));
+ client->fd = fd;
+
+ printf("add client: %d\n", client->fd);
+
+ fds[numfds].fd = client->fd;
+ fds[numfds].events = POLLIN;
+ fds[numfds].revents = 0;
+ clients[numfds] = client;
+ numfds++;
+ }
+
+ // check clients
+ int remove = 0;
+ for(int i=1;i<numfds;i++) {
+ if((fds[i].revents & POLLIN) == POLLIN) {
+ CtrlClient *client = clients[i];
+ ssize_t r = read(fds[i].fd, client->buf + client->pos, CLIENT_MSG_BUFSIZE - client->pos);
+ if(r <= 0) {
+ printf("remove client: %d\n", fds[i].fd);
+ fds[i].events = 0;
+ remove = 1;
+ } else {
+ client->pos += r;
+
+ int msgret = handle_messages(client);
+ if(msgret == 1) {
+ fds[i].events = 0;
+ remove = 1;
+ } else if(msgret == -1) {
+ abort = 1;
+ }
+ }
+ }
+ }
+
+ if(remove) {
+ int j = 1;
+ for(int i=1;i<numfds;i++) {
+ if(fds[i].events != 0) {
+ fds[j] = fds[i];
+ clients[j] = clients[j];
+ j++;
+ } else {
+ client_free(clients[i]);
+ close(fds[i].fd);
+ }
+ }
+ numfds = j;
+ }
+
+ if(diff >= CLIENT_UPDATE_INTERVALL) {
+ for(int i=1;i<numfds;i++) {
+ client_send_status(clients[i]);
+ }
+ }
+
+ if(abort) break;
+
+ timeout = numfds > 1 ? TIMEOUT_CLIENT : TIMEOUT_IDLE;
+ }
+
+ unlink(socket_path);
+
+ return 0;
+}
+
+
+void client_free(CtrlClient *client) {
+ free(client);
+}
+
+int handle_messages(CtrlClient *client) {
+ if(client->pos == CLIENT_MSG_BUFSIZE) {
+ return 1;
+ }
+
+ int msgstart = 0;
+ for(int i=0;i<client->pos;i++) {
+ if(client->buf[i] == '\n') {
+ sstr_t msg;
+ msg.ptr = &client->buf[msgstart];
+ msg.length = i - msgstart;
+ msgstart = i+1;
+
+ int msgret = handle_client_msg(client, msg);
+ if(msgret) return msgret;
+ }
+ }
+
+ if(msgstart < client->pos) {
+ // incomplete message
+ memmove(client->buf, client->buf + msgstart, client->pos - msgstart);
+ client->pos -= msgstart;
+ } else {
+ client->pos = 0;
+ }
+
+ return 0;
+}
+
+int handle_client_msg(CtrlClient *client, sstr_t msg) {
+ printf("msg: %.*s\n", (int)msg.length, msg.ptr);
+
+ if(!sstrcmp(msg, S("abort"))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void client_send_status(CtrlClient *client) {
+ char *msg = "s 0\n";
+ write(client->fd, msg, strlen(msg));
+}