move control socket handling to separate file
[mizunara.git] / mizucp / srvctrl.c
diff --git a/mizucp/srvctrl.c b/mizucp/srvctrl.c
new file mode 100644 (file)
index 0000000..4b3bbd9
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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));
+}