9dc305afbf532769fafba9891b89809a3327b4fd
[mizunara.git] / mizucp / main.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 "main.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 <libidav/utils.h>
46
47 #include <ucx/utils.h>
48
49 #define OPTSTR "hlpsuv"
50
51 #define TIMEOUT_IDLE -1
52 #define TIMEOUT_CLIENT 1000
53 #define CLIENT_UPDATE_INTERVALL 1
54
55 static char *cfgdir;
56 static char *socket_path;
57
58 static int srvctrl;
59
60 static int eventp[2];
61
62 int main(int argc, char** argv) { 
63     int ret = 1;
64     
65     extern char *optarg;
66     extern int optind, opterr, optopt;
67     
68     CPSettings settings;
69     memset(&settings, 0, sizeof(CPSettings));
70     
71     int help = 0;
72     int version = 0;
73     int list = 0;    // list copying processes
74     
75     int c;
76     while((c = getopt(argc, argv, OPTSTR)) != -1) {
77         switch(c) {
78             case 'l': list = 1; break;
79             case 'p': settings.pause = 1; break;
80             case 's': settings.printsocket = 1; break;
81             case 'u': settings.url = 1; break;
82             case 'v': version = 1; break;
83         }
84     }
85     
86     int ac = argc - optind;
87     
88     if(list) {
89         // list command
90     } else if(help) {
91         // print help
92     } else if(version) {
93         // print version
94     } else if(ac == 2) {
95         // copy
96         settings.from = argv[optind];
97         settings.to   = argv[optind+1];
98         ret = uwcp_copy(&settings);
99     } else {
100         
101         // print usage
102     }
103     
104     return ret;
105 }
106
107 static int check_configdir(void) {
108     char *home = getenv(UWCP_ENV_HOME);
109     
110     cfgdir = util_concat_path(home, UWCP_CFG_DIR);
111     
112     struct stat s;
113     if(stat(cfgdir, &s)) {
114         if(errno == ENOENT) {
115             if(mkdir(cfgdir, S_IRWXU)) {
116                 fprintf(stderr, "Cannot create %s: %s", cfgdir, strerror(errno));
117                 return 1;
118             }
119         } else {
120             fprintf(stderr, "Cannot access %s: %s", cfgdir, strerror(errno));
121             return 1;
122         }
123     }
124     
125     return 0;
126 }
127
128 static int create_control_socket(void) {
129     char *copydir = util_concat_path(cfgdir, UWCP_COPY_DIR);
130     
131     struct stat s;
132     if(stat(copydir, &s)) {
133         if(errno == ENOENT) {
134             if(mkdir(copydir, S_IRWXU)) {
135                 fprintf(stderr, "Cannot create %s: %s", copydir, strerror(errno));
136                 return 1;
137             }
138         } else {
139             fprintf(stderr, "Cannot access %s: %s", copydir, strerror(errno));
140             return 1;
141         }
142     }
143     
144     // create unix domain socket
145     char *random_str = util_random_str();
146     sstr_t socketp = ucx_sprintf("%s/%.*s", copydir, 8, random_str);
147     free(random_str);
148     socket_path = socketp.ptr;
149     
150     struct sockaddr_un addr;
151     if(socketp.length > sizeof(addr.sun_path)-1) {
152         fprintf(stderr,
153                 "path '%s' too long for unix domain socket",
154                 socketp.ptr);
155         return 1;
156     }
157     
158     memset(&addr, 0, sizeof(addr));
159     addr.sun_family = AF_UNIX;
160     memcpy(addr.sun_path, socketp.ptr, socketp.length);
161     
162     srvctrl = socket(AF_UNIX, SOCK_STREAM, 0);
163     if(srvctrl == -1) {
164         fprintf(stderr,
165                 "Cannot create server control socket: %s",
166                 strerror(errno));
167         return 1;
168     }
169     if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) {
170         fprintf(stderr,
171                 "srvctrl socket bind failed: %s",
172                 strerror(errno));
173         return 1;
174     }
175     
176     listen(srvctrl, 4);
177     
178     return 0;
179 }
180
181 int uwcp_copy(CPSettings *settings) {
182     int ret  = 0;
183     
184     if(check_configdir()) {
185         return 2;
186     }
187     
188     
189     if(create_control_socket()) {
190         return 3;
191     }
192     
193     if(settings->printsocket) {
194         printf("%s\n", socket_path);
195     } else {
196         printf("copy %s to %s\n", settings->from, settings->to);
197         if(settings->pause) {
198             printf("pause\n");
199         }
200     }
201     
202     //pid_t p = fork();
203     pid_t p = 0;
204     if(p == 0) {
205         //close(0);
206         //close(1);
207         //close(2);
208         
209         ret =  uwcp_srvctrl(settings);
210     }
211     
212     return ret;
213 }
214
215 int uwcp_srvctrl(CPSettings *settings) {
216     if(pipe(eventp)) {
217         perror("Cannot create event pipe");
218         return 1;
219     }
220     
221     size_t allocfds = 8;
222     size_t numfds = 1;
223     
224     struct pollfd *fds = calloc(allocfds, sizeof(struct pollfd));
225     CtrlClient **clients = calloc(allocfds, sizeof(void*));
226     
227     int timeout = TIMEOUT_IDLE;
228     
229     fds[0].fd = srvctrl;
230     fds[0].events = POLLIN;
231     
232     int abort = 0;
233     
234     time_t tbegin = time(NULL);
235     
236     while(poll(fds, numfds, 1000) >= 0) {
237         time_t tend = time(NULL);
238         time_t diff = tend - tbegin;
239         tbegin = tend;
240         
241         if((fds[0].revents & POLLIN) == POLLIN) {
242             printf("accept\n");
243             int fd = accept(srvctrl, NULL, 0);
244             if(fd < 0) {
245                 break;
246             }
247             
248             //int flags = fcntl(fd, F_GETFL, 0);
249             //flags = flags & ~O_NONBLOCK;
250             //fcntl(fd, F_SETFL, flags);
251             
252             CtrlClient *client = malloc(sizeof(CtrlClient));
253             memset(client, 0, sizeof(CtrlClient));
254             client->fd = fd;
255             
256             printf("add client: %d\n", client->fd);
257             
258             fds[numfds].fd = client->fd;
259             fds[numfds].events = POLLIN;
260             fds[numfds].revents = 0;
261             clients[numfds] = client;
262             numfds++;
263         }
264         
265         // check clients
266         int remove = 0;
267         for(int i=1;i<numfds;i++) {
268             if((fds[i].revents & POLLIN) == POLLIN) {
269                 CtrlClient *client = clients[i];
270                 ssize_t r = read(fds[i].fd, client->buf + client->pos, CLIENT_MSG_BUFSIZE - client->pos);
271                 if(r <= 0) {
272                     printf("remove client: %d\n", fds[i].fd);
273                     fds[i].events = 0;
274                     remove = 1;
275                 } else {
276                     client->pos += r;
277                     
278                     int msgret = handle_messages(client);
279                     if(msgret == 1) {
280                         fds[i].events = 0;
281                         remove = 1;
282                     } else if(msgret == -1) {
283                         abort = 1;
284                     }
285                 }
286             }
287         }
288         
289         if(remove) {
290             int j = 1;
291             for(int i=1;i<numfds;i++) {
292                 if(fds[i].events != 0) {
293                     fds[j] = fds[i];
294                     clients[j] = clients[j];
295                     j++;
296                 } else {
297                     client_free(clients[i]);
298                     close(fds[i].fd);
299                 }
300             }
301             numfds = j;
302         }
303         
304         if(diff >= CLIENT_UPDATE_INTERVALL) {
305             for(int i=1;i<numfds;i++) {
306                 client_send_status(clients[i]);
307             }
308         }
309         
310         if(abort) break;
311         
312         timeout = numfds > 1 ? TIMEOUT_CLIENT : TIMEOUT_IDLE;
313     }
314     
315     unlink(socket_path);
316     
317     return 0;
318 }
319
320
321 void client_free(CtrlClient *client) {
322     free(client);
323 }
324
325 int handle_messages(CtrlClient *client) {
326     if(client->pos == CLIENT_MSG_BUFSIZE) {
327         return 1;
328     }
329     
330     int msgstart = 0;
331     for(int i=0;i<client->pos;i++) {
332         if(client->buf[i] == '\n') {
333             sstr_t msg;
334             msg.ptr = &client->buf[msgstart];
335             msg.length = i - msgstart;
336             msgstart = i+1;
337             
338             int msgret = handle_client_msg(client, msg);
339             if(msgret) return msgret;
340         }
341     }
342     
343     if(msgstart < client->pos) {
344         // incomplete message
345         memmove(client->buf, client->buf + msgstart, client->pos - msgstart);
346         client->pos -= msgstart;
347     } else {
348         client->pos = 0;
349     }
350     
351     return 0;
352 }
353
354 int handle_client_msg(CtrlClient *client, sstr_t msg) {
355     printf("msg: %.*s\n", (int)msg.length, msg.ptr);
356     
357     if(!sstrcmp(msg, S("abort"))) {
358         return -1;
359     }
360     
361     return 0;
362 }
363
364 void client_send_status(CtrlClient *client) {
365     char *msg = "s 0\n";
366     write(client->fd, msg, strlen(msg));
367 }