diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/evas/src/lib/cserve/evas_cs_server.c | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/libraries/evas/src/lib/cserve/evas_cs_server.c b/libraries/evas/src/lib/cserve/evas_cs_server.c new file mode 100644 index 0000000..3b5d8b9 --- /dev/null +++ b/libraries/evas/src/lib/cserve/evas_cs_server.c | |||
@@ -0,0 +1,380 @@ | |||
1 | #ifdef HAVE_CONFIG_H | ||
2 | # include "config.h" | ||
3 | #endif | ||
4 | |||
5 | #include "evas_cs.h" | ||
6 | |||
7 | #ifdef EVAS_CSERVE | ||
8 | |||
9 | EAPI Server * | ||
10 | evas_cserve_server_add(void) | ||
11 | { | ||
12 | Server *s; | ||
13 | char buf[PATH_MAX]; | ||
14 | struct sockaddr_un socket_unix; | ||
15 | struct linger lin; | ||
16 | mode_t pmode; | ||
17 | int socket_unix_len; | ||
18 | |||
19 | s = calloc(1, sizeof(Server)); | ||
20 | if (!s) return NULL; | ||
21 | s->ch[0].fd = -1; | ||
22 | snprintf(buf, sizeof(buf), "/tmp/.evas-cserve-%x", getuid()); | ||
23 | s->socket_path = strdup(buf); | ||
24 | if (!s->socket_path) | ||
25 | { | ||
26 | free(s); | ||
27 | return NULL; | ||
28 | } | ||
29 | pmode = umask(~(S_IRUSR | S_IWUSR)); | ||
30 | start: | ||
31 | s->ch[0].fd = socket(AF_UNIX, SOCK_STREAM, 0); | ||
32 | if (s->ch[0].fd < 0) goto error; | ||
33 | if (fcntl(s->ch[0].fd, F_SETFL, O_NONBLOCK) < 0) goto error; | ||
34 | if (fcntl(s->ch[0].fd, F_SETFD, FD_CLOEXEC) < 0) goto error; | ||
35 | lin.l_onoff = 1; | ||
36 | lin.l_linger = 0; | ||
37 | if (setsockopt(s->ch[0].fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0) | ||
38 | goto error; | ||
39 | socket_unix.sun_family = AF_UNIX; | ||
40 | strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path)); | ||
41 | socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix); | ||
42 | if (bind(s->ch[0].fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) | ||
43 | { | ||
44 | if ((connect(s->ch[0].fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) && | ||
45 | (unlink(s->socket_path) >= 0)) | ||
46 | { | ||
47 | close(s->ch[0].fd); | ||
48 | goto start; | ||
49 | } | ||
50 | else | ||
51 | goto error; | ||
52 | } | ||
53 | if (listen(s->ch[0].fd, 4096) < 0) goto error; | ||
54 | umask(pmode); | ||
55 | return s; | ||
56 | error: | ||
57 | umask(pmode); | ||
58 | if (s->ch[0].fd >= 0) close(s->ch[0].fd); | ||
59 | free(s->socket_path); | ||
60 | free(s); | ||
61 | return NULL; | ||
62 | } | ||
63 | |||
64 | EAPI void | ||
65 | evas_cserve_server_del(Server *s) | ||
66 | { | ||
67 | Client *c; | ||
68 | |||
69 | EINA_LIST_FREE(s->clients, c) | ||
70 | { | ||
71 | LKL(c->lock); | ||
72 | close(c->fd); | ||
73 | if (c->buf) free(c->buf); | ||
74 | if (c->inbuf) free(c->inbuf); | ||
75 | LKD(c->lock); | ||
76 | free(c); | ||
77 | } | ||
78 | close(s->ch[0].fd); | ||
79 | unlink(s->socket_path); | ||
80 | free(s->socket_path); | ||
81 | free(s); | ||
82 | } | ||
83 | |||
84 | static void | ||
85 | server_accept(Server *s) | ||
86 | { | ||
87 | Client *c; | ||
88 | int new_fd; | ||
89 | struct sockaddr_in incoming; | ||
90 | size_t size_in; | ||
91 | |||
92 | size_in = sizeof(struct sockaddr_in); | ||
93 | new_fd = accept(s->ch[0].fd, (struct sockaddr *)&incoming, (socklen_t *)&size_in); | ||
94 | if (new_fd < 0) return; | ||
95 | fcntl(new_fd, F_SETFL, O_NONBLOCK); | ||
96 | fcntl(new_fd, F_SETFD, FD_CLOEXEC); | ||
97 | c = calloc(1, sizeof(Client)); | ||
98 | if (!c) | ||
99 | { | ||
100 | close(new_fd); | ||
101 | return; | ||
102 | } | ||
103 | c->server = s; | ||
104 | c->fd = new_fd; | ||
105 | LKI(c->lock); | ||
106 | s->clients = eina_list_append(s->clients, c); | ||
107 | } | ||
108 | |||
109 | static void | ||
110 | client_flush(Client *c) | ||
111 | { | ||
112 | int num; | ||
113 | |||
114 | num = write(c->fd, c->buf, c->bufsize); | ||
115 | if (num < 0) | ||
116 | { | ||
117 | c->dead = 1; | ||
118 | return; | ||
119 | } | ||
120 | if (num < c->bufsize) | ||
121 | { | ||
122 | unsigned char *buf; | ||
123 | |||
124 | buf = malloc(c->bufsize - num); | ||
125 | if (buf) | ||
126 | { | ||
127 | memcpy(buf, c->buf + num, c->bufsize - num); | ||
128 | free(c->buf); | ||
129 | c->bufsize = c->bufsize - num; | ||
130 | c->bufalloc = c->bufsize; | ||
131 | c->buf = buf; | ||
132 | } | ||
133 | } | ||
134 | else | ||
135 | { | ||
136 | free(c->buf); | ||
137 | c->buf = NULL; | ||
138 | c->bufsize = 0; | ||
139 | c->bufalloc = 0; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static void | ||
144 | client_buf_add(Client *c, unsigned char *data, int size) | ||
145 | { | ||
146 | int newsize; | ||
147 | unsigned char *buf; | ||
148 | |||
149 | newsize = c->bufsize + size; | ||
150 | if (newsize > c->bufalloc) | ||
151 | { | ||
152 | c->bufalloc = newsize + 16384; | ||
153 | buf = realloc(c->buf, c->bufalloc); | ||
154 | if (buf) c->buf = buf; | ||
155 | else return; | ||
156 | } | ||
157 | memcpy(c->buf + c->bufsize, data, size); | ||
158 | c->bufsize += size; | ||
159 | } | ||
160 | |||
161 | static void | ||
162 | client_write(Client *c, unsigned char *data, int size) | ||
163 | { | ||
164 | int num; | ||
165 | |||
166 | if (!c->buf) | ||
167 | { | ||
168 | num = write(c->fd, data, size); | ||
169 | if (num != size) | ||
170 | client_buf_add(c, data + num, size - num); | ||
171 | } | ||
172 | else | ||
173 | { | ||
174 | client_buf_add(c, data, size); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | EAPI void | ||
179 | evas_cserve_client_send(Client *c, int opcode, int size, unsigned char *data) | ||
180 | { | ||
181 | unsigned char *data2; | ||
182 | int *ints; | ||
183 | |||
184 | data2 = malloc(size + (sizeof(int) * 3)); | ||
185 | if (!data2) return; | ||
186 | ints = (int *)data2; | ||
187 | ints[0] = size; | ||
188 | ints[1] = opcode; | ||
189 | // LKL(c->lock); | ||
190 | c->req_to++; | ||
191 | ints[2] = c->req_to; | ||
192 | memcpy(data2 + (sizeof(int) * 3), data, size); | ||
193 | client_write(c, data2, size + (sizeof(int) * 3)); | ||
194 | // LKU(c->lock); | ||
195 | free(data2); | ||
196 | } | ||
197 | |||
198 | static void | ||
199 | server_message_handle(Server *s, Client *c, int opcode, int size, unsigned char *data) | ||
200 | { | ||
201 | if (s->func) s->func(s->data, s, c, opcode, size, data); | ||
202 | } | ||
203 | |||
204 | EAPI void | ||
205 | evas_cserve_server_message_handler_set(Server *s, int (*func) (void *fdata, Server *s, Client *c, int opcode, int size, unsigned char *data), void *data) | ||
206 | { | ||
207 | s->func = func; | ||
208 | s->data = data; | ||
209 | } | ||
210 | |||
211 | static int | ||
212 | server_parse(Server *s, Client *c) | ||
213 | { | ||
214 | int *ints; | ||
215 | unsigned char *data, *newbuf; | ||
216 | |||
217 | if (c->inbufsize < (int)sizeof(int)) return 0; | ||
218 | ints = (int *)((c->inbuf)); | ||
219 | if ((ints[0] < 0) || (ints[0] > (1024 * 1024))) | ||
220 | return 0; | ||
221 | if (c->inbufsize < (ints[0] + ((int)sizeof(int) * 3))) | ||
222 | { | ||
223 | return 0; | ||
224 | } | ||
225 | data = c->inbuf + (sizeof(int) * 3); | ||
226 | if (ints[2] != (c->req_from + 1)) | ||
227 | { | ||
228 | ERR("EEK! sequence number mismatch from client with pid: %i." | ||
229 | "---- num %i is not 1 more than %i" | ||
230 | , | ||
231 | c->pid, ints[2], c->req_from); | ||
232 | return 0; | ||
233 | } | ||
234 | c->req_from++; | ||
235 | server_message_handle(s, c, ints[1], ints[0], data); | ||
236 | c->inbufalloc -= ints[0] + (sizeof(int) * 3); | ||
237 | if (c->inbufalloc == 0) | ||
238 | { | ||
239 | free(c->inbuf); | ||
240 | c->inbuf = NULL; | ||
241 | c->inbufsize = 0; | ||
242 | return 0; | ||
243 | } | ||
244 | newbuf = malloc(c->inbufalloc); | ||
245 | if (!newbuf) | ||
246 | { | ||
247 | c->inbufalloc += ints[0] + (sizeof(int) * 3); | ||
248 | /* fixme - bad situation */ | ||
249 | return 0; | ||
250 | } | ||
251 | memcpy(newbuf, c->inbuf + ints[0] + (sizeof(int) * 3), c->inbufalloc); | ||
252 | c->inbufsize -= ints[0] + (sizeof(int) * 3); | ||
253 | free(c->inbuf); | ||
254 | c->inbuf = newbuf; | ||
255 | return 1; | ||
256 | } | ||
257 | |||
258 | static void | ||
259 | server_data(Server *s, Client *c, unsigned char *data, int size) | ||
260 | { | ||
261 | if (!c->inbuf) | ||
262 | { | ||
263 | c->inbuf = malloc(size); | ||
264 | if (c->inbuf) | ||
265 | { | ||
266 | memcpy(c->inbuf, data, size); | ||
267 | c->inbufalloc = size; | ||
268 | c->inbufsize = size; | ||
269 | } | ||
270 | else | ||
271 | { | ||
272 | /* fixme - bad situation */ | ||
273 | return; | ||
274 | } | ||
275 | } | ||
276 | else | ||
277 | { | ||
278 | int size2; | ||
279 | |||
280 | size2 = c->inbufsize + size; | ||
281 | if (size2 > c->inbufalloc) | ||
282 | { | ||
283 | unsigned char *newbuf; | ||
284 | |||
285 | c->inbufalloc = size2; | ||
286 | newbuf = realloc(c->inbuf, c->inbufalloc); | ||
287 | if (newbuf) c->inbuf = newbuf; | ||
288 | else size2 = 0; | ||
289 | } | ||
290 | if (size2 > 0) | ||
291 | { | ||
292 | memcpy(c->inbuf + c->inbufsize, data, size); | ||
293 | c->inbufsize = size2; | ||
294 | } | ||
295 | else | ||
296 | { | ||
297 | /* fixme - bad situation */ | ||
298 | return; | ||
299 | } | ||
300 | } | ||
301 | while (server_parse(s, c)); | ||
302 | } | ||
303 | |||
304 | EAPI void | ||
305 | evas_cserve_server_wait(Server *s, int timeout) | ||
306 | { | ||
307 | fd_set rset, wset, xset; | ||
308 | int maxfd; | ||
309 | int ret; | ||
310 | struct timeval to; | ||
311 | Eina_List *l, *dead = NULL; | ||
312 | Client *c; | ||
313 | |||
314 | maxfd = 0; | ||
315 | FD_ZERO(&rset); | ||
316 | FD_ZERO(&wset); | ||
317 | FD_ZERO(&xset); | ||
318 | FD_SET(s->ch[0].fd, &rset); | ||
319 | if (s->ch[0].fd > maxfd) maxfd = s->ch[0].fd; | ||
320 | EINA_LIST_FOREACH(s->clients, l, c) | ||
321 | { | ||
322 | FD_SET(c->fd, &rset); | ||
323 | if (c->buf) | ||
324 | FD_SET(c->fd, &wset); | ||
325 | if (c->fd > maxfd) maxfd = c->fd; | ||
326 | } | ||
327 | if (timeout > 0) | ||
328 | { | ||
329 | to.tv_sec = timeout / 1000000; | ||
330 | to.tv_usec = timeout % 1000000; | ||
331 | ret = select(maxfd + 1, &rset, &wset, &xset, &to); | ||
332 | } | ||
333 | else | ||
334 | ret = select(maxfd + 1, &rset, &wset, &xset, NULL); | ||
335 | if (ret < 1) return; | ||
336 | |||
337 | EINA_LIST_FOREACH(s->clients, l, c) | ||
338 | { | ||
339 | if (c->dead) continue; | ||
340 | if (FD_ISSET(c->fd, &rset)) | ||
341 | { | ||
342 | unsigned char buf[16384]; | ||
343 | int num; | ||
344 | |||
345 | errno = 0; | ||
346 | num = read(c->fd, buf, sizeof(buf)); | ||
347 | if (num <= 0) | ||
348 | { | ||
349 | c->dead = 1; | ||
350 | dead = eina_list_append(dead, c); | ||
351 | } | ||
352 | else if (num > 0) | ||
353 | { | ||
354 | server_data(s, c, buf, num); | ||
355 | } | ||
356 | } | ||
357 | else if (FD_ISSET(c->fd, &wset)) | ||
358 | { | ||
359 | client_flush(c); | ||
360 | if (c->dead) dead = eina_list_append(dead, c); | ||
361 | } | ||
362 | } | ||
363 | if (FD_ISSET(s->ch[0].fd, &rset)) | ||
364 | { | ||
365 | server_accept(s); | ||
366 | } | ||
367 | EINA_LIST_FREE(dead, c) | ||
368 | { | ||
369 | LKL(c->lock); | ||
370 | if (c->func) c->func(c->data, c); | ||
371 | s->clients = eina_list_remove(s->clients, c); | ||
372 | close(c->fd); | ||
373 | if (c->buf) free(c->buf); | ||
374 | if (c->inbuf) free(c->inbuf); | ||
375 | LKD(c->lock); | ||
376 | free(c); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | #endif | ||