move control socket handling to separate file
[mizunara.git] / mizucp / srvctrl.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2021 Olaf Wintermann. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "srvctrl.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <sys/stat.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #include <sys/fcntl.h>
42 #include <pthread.h>
43 #include <poll.h>
44
45 #include <ucx/utils.h>
46 #include <libidav/utils.h>
47
48 #define TIMEOUT_IDLE -1
49 #define TIMEOUT_CLIENT 1000
50 #define CLIENT_UPDATE_INTERVALL 1
51
52 static char *socket_path;
53
54 static int srvctrl;
55
56
57 int create_control_socket(void) {
58     const char *copydir = mzcp_get_copydir();
59     
60     // create unix domain socket
61     char *random_str = util_random_str();
62     sstr_t socketp = ucx_sprintf("%s/%.*s", copydir, 8, random_str);
63     free(random_str);
64     socket_path = socketp.ptr;
65     
66     struct sockaddr_un addr;
67     if(socketp.length > sizeof(addr.sun_path)-1) {
68         fprintf(stderr,
69                 "path '%s' too long for unix domain socket",
70                 socketp.ptr);
71         return 1;
72     }
73     
74     memset(&addr, 0, sizeof(addr));
75     addr.sun_family = AF_UNIX;
76     memcpy(addr.sun_path, socketp.ptr, socketp.length);
77     
78     srvctrl = socket(AF_UNIX, SOCK_STREAM, 0);
79     if(srvctrl == -1) {
80         fprintf(stderr,
81                 "Cannot create server control socket: %s",
82                 strerror(errno));
83         return 1;
84     }
85     if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) {
86         fprintf(stderr,
87                 "srvctrl socket bind failed: %s",
88                 strerror(errno));
89         return 1;
90     }
91     
92     listen(srvctrl, 4);
93     
94     return 0;
95 }
96
97 const char* mzcp_get_socketpath(void) {
98     return socket_path;
99 }
100
101 int mzcp_srvctrl(CPSettings *settings) {
102     
103     size_t allocfds = 8;
104     size_t numfds = 1;
105     
106     struct pollfd *fds = calloc(allocfds, sizeof(struct pollfd));
107     CtrlClient **clients = calloc(allocfds, sizeof(void*));
108     
109     int timeout = TIMEOUT_IDLE;
110     
111     fds[0].fd = srvctrl;
112     fds[0].events = POLLIN;
113     
114     int abort = 0;
115     
116     time_t tbegin = time(NULL);
117     
118     while(poll(fds, numfds, 1000) >= 0) {
119         time_t tend = time(NULL);
120         time_t diff = tend - tbegin;
121         tbegin = tend;
122         
123         if((fds[0].revents & POLLIN) == POLLIN) {
124             printf("accept\n");
125             int fd = accept(srvctrl, NULL, 0);
126             if(fd < 0) {
127                 break;
128             }
129             
130             //int flags = fcntl(fd, F_GETFL, 0);
131             //flags = flags & ~O_NONBLOCK;
132             //fcntl(fd, F_SETFL, flags);
133             
134             CtrlClient *client = malloc(sizeof(CtrlClient));
135             memset(client, 0, sizeof(CtrlClient));
136             client->fd = fd;
137             
138             printf("add client: %d\n", client->fd);
139             
140             fds[numfds].fd = client->fd;
141             fds[numfds].events = POLLIN;
142             fds[numfds].revents = 0;
143             clients[numfds] = client;
144             numfds++;
145         }
146         
147         // check clients
148         int remove = 0;
149         for(int i=1;i<numfds;i++) {
150             if((fds[i].revents & POLLIN) == POLLIN) {
151                 CtrlClient *client = clients[i];
152                 ssize_t r = read(fds[i].fd, client->buf + client->pos, CLIENT_MSG_BUFSIZE - client->pos);
153                 if(r <= 0) {
154                     printf("remove client: %d\n", fds[i].fd);
155                     fds[i].events = 0;
156                     remove = 1;
157                 } else {
158                     client->pos += r;
159                     
160                     int msgret = handle_messages(client);
161                     if(msgret == 1) {
162                         fds[i].events = 0;
163                         remove = 1;
164                     } else if(msgret == -1) {
165                         abort = 1;
166                     }
167                 }
168             }
169         }
170         
171         if(remove) {
172             int j = 1;
173             for(int i=1;i<numfds;i++) {
174                 if(fds[i].events != 0) {
175                     fds[j] = fds[i];
176                     clients[j] = clients[j];
177                     j++;
178                 } else {
179                     client_free(clients[i]);
180                     close(fds[i].fd);
181                 }
182             }
183             numfds = j;
184         }
185         
186         if(diff >= CLIENT_UPDATE_INTERVALL) {
187             for(int i=1;i<numfds;i++) {
188                 client_send_status(clients[i]);
189             }
190         }
191         
192         if(abort) break;
193         
194         timeout = numfds > 1 ? TIMEOUT_CLIENT : TIMEOUT_IDLE;
195     }
196     
197     unlink(socket_path);
198     
199     return 0;
200 }
201
202
203 void client_free(CtrlClient *client) {
204     free(client);
205 }
206
207 int handle_messages(CtrlClient *client) {
208     if(client->pos == CLIENT_MSG_BUFSIZE) {
209         return 1;
210     }
211     
212     int msgstart = 0;
213     for(int i=0;i<client->pos;i++) {
214         if(client->buf[i] == '\n') {
215             sstr_t msg;
216             msg.ptr = &client->buf[msgstart];
217             msg.length = i - msgstart;
218             msgstart = i+1;
219             
220             int msgret = handle_client_msg(client, msg);
221             if(msgret) return msgret;
222         }
223     }
224     
225     if(msgstart < client->pos) {
226         // incomplete message
227         memmove(client->buf, client->buf + msgstart, client->pos - msgstart);
228         client->pos -= msgstart;
229     } else {
230         client->pos = 0;
231     }
232     
233     return 0;
234 }
235
236 int handle_client_msg(CtrlClient *client, sstr_t msg) {
237     printf("msg: %.*s\n", (int)msg.length, msg.ptr);
238     
239     if(!sstrcmp(msg, S("abort"))) {
240         return -1;
241     }
242     
243     return 0;
244 }
245
246 void client_send_status(CtrlClient *client) {
247     char *msg = "s 0\n";
248     write(client->fd, msg, strlen(msg));
249 }