mizucp: implement scan thread
[mizunara.git] / mizucp / srvctrl.c
1 /*
2  * Copyright 2019 Olaf Wintermann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a 
5  * copy of this software and associated documentation files (the "Software"), 
6  * to deal in the Software without restriction, including without limitation 
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8  * and/or sell copies of the Software, and to permit persons to whom the 
9  * Software is furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
20  * DEALINGS IN THE SOFTWARE.
21  */
22 #include "srvctrl.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <sys/fcntl.h>
35 #include <pthread.h>
36 #include <poll.h>
37
38 #include <ucx/utils.h>
39 #include <libidav/utils.h>
40
41 #define TIMEOUT_IDLE -1
42 #define TIMEOUT_CLIENT 1000
43 #define CLIENT_UPDATE_INTERVALL 1
44
45 static char *socket_path;
46
47 static int srvctrl;
48
49
50 int create_control_socket(void) { 
51     const char *copydir = mzcp_get_copydir();
52     
53     // create unix domain socket
54     char *random_str = util_random_str();
55     sstr_t socketp = ucx_sprintf("%s/%.*s", copydir, 8, random_str);
56     free(random_str);
57     socket_path = socketp.ptr;
58     
59     struct sockaddr_un addr;
60     if(socketp.length > sizeof(addr.sun_path)-1) {
61         fprintf(stderr,
62                 "path '%s' too long for unix domain socket",
63                 socketp.ptr);
64         return 1;
65     }
66     
67     memset(&addr, 0, sizeof(addr));
68     addr.sun_family = AF_UNIX;
69     memcpy(addr.sun_path, socketp.ptr, socketp.length);
70     
71     srvctrl = socket(AF_UNIX, SOCK_STREAM, 0);
72     if(srvctrl == -1) {
73         fprintf(stderr,
74                 "Cannot create server control socket: %s",
75                 strerror(errno));
76         return 1;
77     }
78     if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) {
79         fprintf(stderr,
80                 "srvctrl socket bind failed: %s",
81                 strerror(errno));
82         return 1;
83     }
84     
85     listen(srvctrl, 4);
86     
87     return 0;
88 }
89
90 const char* mzcp_get_socketpath(void) {
91     return socket_path;
92 }
93
94 int mzcp_srvctrl(CPSettings *settings) {
95     
96     size_t allocfds = 8;
97     size_t numfds = 1;
98     
99     struct pollfd *fds = calloc(allocfds, sizeof(struct pollfd));
100     CtrlClient **clients = calloc(allocfds, sizeof(void*));
101     
102     int timeout = TIMEOUT_IDLE;
103     
104     fds[0].fd = srvctrl;
105     fds[0].events = POLLIN;
106     
107     int abort = 0;
108     
109     time_t tbegin = time(NULL);
110     
111     while(poll(fds, numfds, 1000) >= 0) {
112         time_t tend = time(NULL);
113         time_t diff = tend - tbegin;
114         tbegin = tend;
115         
116         if((fds[0].revents & POLLIN) == POLLIN) {
117             printf("accept\n");
118             int fd = accept(srvctrl, NULL, 0);
119             if(fd < 0) {
120                 break;
121             }
122             
123             //int flags = fcntl(fd, F_GETFL, 0);
124             //flags = flags & ~O_NONBLOCK;
125             //fcntl(fd, F_SETFL, flags);
126             
127             CtrlClient *client = malloc(sizeof(CtrlClient));
128             memset(client, 0, sizeof(CtrlClient));
129             client->fd = fd;
130             
131             printf("add client: %d\n", client->fd);
132             
133             fds[numfds].fd = client->fd;
134             fds[numfds].events = POLLIN;
135             fds[numfds].revents = 0;
136             clients[numfds] = client;
137             numfds++;
138         }
139         
140         // check clients
141         int remove = 0;
142         for(int i=1;i<numfds;i++) {
143             if((fds[i].revents & POLLIN) == POLLIN) {
144                 CtrlClient *client = clients[i];
145                 ssize_t r = read(fds[i].fd, client->buf + client->pos, CLIENT_MSG_BUFSIZE - client->pos);
146                 if(r <= 0) {
147                     printf("remove client: %d\n", fds[i].fd);
148                     fds[i].events = 0;
149                     remove = 1;
150                 } else {
151                     client->pos += r;
152                     
153                     int msgret = handle_messages(client);
154                     if(msgret == 1) {
155                         fds[i].events = 0;
156                         remove = 1;
157                     } else if(msgret == -1) {
158                         abort = 1;
159                     }
160                 }
161             }
162         }
163         
164         if(remove) {
165             int j = 1;
166             for(int i=1;i<numfds;i++) {
167                 if(fds[i].events != 0) {
168                     fds[j] = fds[i];
169                     clients[j] = clients[j];
170                     j++;
171                 } else {
172                     client_free(clients[i]);
173                     close(fds[i].fd);
174                 }
175             }
176             numfds = j;
177         }
178         
179         if(diff >= CLIENT_UPDATE_INTERVALL) {
180             for(int i=1;i<numfds;i++) {
181                 client_send_status(clients[i]);
182             }
183         }
184         
185         if(abort) break;
186         
187         timeout = numfds > 1 ? TIMEOUT_CLIENT : TIMEOUT_IDLE;
188     }
189     
190     unlink(socket_path);
191     
192     return 0;
193 }
194
195
196 void client_free(CtrlClient *client) {
197     free(client);
198 }
199
200 int handle_messages(CtrlClient *client) {
201     if(client->pos == CLIENT_MSG_BUFSIZE) {
202         return 1;
203     }
204     
205     int msgstart = 0;
206     for(int i=0;i<client->pos;i++) {
207         if(client->buf[i] == '\n') {
208             sstr_t msg;
209             msg.ptr = &client->buf[msgstart];
210             msg.length = i - msgstart;
211             msgstart = i+1;
212             
213             int msgret = handle_client_msg(client, msg);
214             if(msgret) return msgret;
215         }
216     }
217     
218     if(msgstart < client->pos) {
219         // incomplete message
220         memmove(client->buf, client->buf + msgstart, client->pos - msgstart);
221         client->pos -= msgstart;
222     } else {
223         client->pos = 0;
224     }
225     
226     return 0;
227 }
228
229 int handle_client_msg(CtrlClient *client, sstr_t msg) {
230     printf("msg: %.*s\n", (int)msg.length, msg.ptr);
231     
232     if(!sstrcmp(msg, S("abort"))) {
233         return -1;
234     }
235     
236     return 0;
237 }
238
239 void client_send_status(CtrlClient *client) {
240     char *msg = "s 0\n";
241     write(client->fd, msg, strlen(msg));
242 }