Fix mouse wheel
[vd_agent/hramrach.git] / udscs.c
blob68e6e342a01f09b0cd11619cde398df298963d95
1 /* udscs.c Unix Domain Socket Client Server framework. A framework for quickly
2 creating select() based servers capable of handling multiple clients and
3 matching select() based clients using variable size messages.
5 Copyright 2010 Red Hat, Inc.
7 Red Hat Authors:
8 Hans de Goede <hdegoede@redhat.com>
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include "udscs.h"
33 struct udscs_buf {
34 uint8_t *buf;
35 size_t pos;
36 size_t size;
38 struct udscs_buf *next;
41 struct udscs_connection {
42 int fd;
44 /* Read stuff, single buffer, separate header and data buffer */
45 int header_read;
46 struct udscs_message_header header;
47 struct udscs_buf data;
49 /* Writes are stored in a linked list of buffers, with both the header
50 + data for a single message in 1 buffer. */
51 struct udscs_buf *write_buf;
53 /* Callbacks */
54 udscs_read_callback read_callback;
55 udscs_disconnect_callback disconnect_callback;
57 struct udscs_connection *next;
58 struct udscs_connection *prev;
61 struct udscs_server {
62 int fd;
63 struct udscs_connection connections_head;
64 udscs_read_callback read_callback;
65 udscs_disconnect_callback disconnect_callback;
68 static void udscs_do_write(struct udscs_connection **connp);
69 static void udscs_do_read(struct udscs_connection **connp);
72 struct udscs_server *udscs_create_server(const char *socketname,
73 udscs_read_callback read_callback,
74 udscs_disconnect_callback disconnect_callback)
76 int c;
77 struct sockaddr_un address;
78 struct udscs_server *server;
80 server = calloc(1, sizeof(*server));
81 if (!server)
82 return NULL;
84 server->fd = socket(PF_UNIX, SOCK_STREAM, 0);
85 if (server->fd == -1) {
86 perror("creating unix domain socket");
87 free(server);
88 return NULL;
91 c = unlink(socketname);
92 if (c != 0 && errno != ENOENT) {
93 fprintf(stderr, "unlink %s: %s\n", socketname, strerror(errno));
94 free(server);
95 return NULL;
98 address.sun_family = AF_UNIX;
99 snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
100 c = bind(server->fd, (struct sockaddr *)&address, sizeof(address));
101 if (c != 0) {
102 fprintf(stderr, "bind %s: %s\n", socketname, strerror(errno));
103 free(server);
104 return NULL;
107 c = listen(server->fd, 5);
108 if (c != 0) {
109 perror("listen");
110 free(server);
111 return NULL;
114 server->read_callback = read_callback;
115 server->disconnect_callback = disconnect_callback;
117 return server;
120 void udscs_destroy_server(struct udscs_server *server)
122 struct udscs_connection *conn, *next_conn;
124 conn = server->connections_head.next;
125 while (conn) {
126 next_conn = conn->next;
127 udscs_destroy_connection(&conn);
128 conn = next_conn;
130 close(server->fd);
131 free(server);
134 struct udscs_connection *udscs_connect(const char *socketname,
135 udscs_read_callback read_callback,
136 udscs_disconnect_callback disconnect_callback)
138 int c;
139 struct sockaddr_un address;
140 struct udscs_connection *conn;
142 conn = calloc(1, sizeof(*conn));
143 if (!conn)
144 return NULL;
146 conn->fd = socket(PF_UNIX, SOCK_STREAM, 0);
147 if (conn->fd == -1) {
148 perror("creating unix domain socket");
149 free(conn);
150 return NULL;
153 address.sun_family = AF_UNIX;
154 snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
155 c = connect(conn->fd, (struct sockaddr *)&address, sizeof(address));
156 if (c != 0) {
157 fprintf(stderr, "connect %s: %s\n", socketname, strerror(errno));
158 free(conn);
159 return NULL;
162 conn->read_callback = read_callback;
163 conn->disconnect_callback = disconnect_callback;
165 return conn;
168 void udscs_destroy_connection(struct udscs_connection **connp)
170 struct udscs_buf *wbuf, *next_wbuf;
171 struct udscs_connection *conn = *connp;
173 if (conn->disconnect_callback)
174 conn->disconnect_callback(conn);
176 wbuf = conn->write_buf;
177 while (wbuf) {
178 next_wbuf = wbuf->next;
179 free(wbuf->buf);
180 free(wbuf);
181 wbuf = next_wbuf;
184 free(conn->data.buf);
186 if (conn->prev)
187 conn->prev->next = conn->next;
189 close(conn->fd);
190 free(conn);
191 *connp = NULL;
194 int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
195 fd_set *writefds)
197 struct udscs_connection *conn;
198 int nfds = server->fd + 1;
200 FD_SET(server->fd, readfds);
202 conn = server->connections_head.next;
203 while (conn) {
204 int conn_nfds = udscs_client_fill_fds(conn, readfds, writefds);
205 if (conn_nfds > nfds)
206 nfds = conn_nfds;
208 conn = conn->next;
211 return nfds;
214 int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds,
215 fd_set *writefds)
217 FD_SET(conn->fd, readfds);
218 if (conn->write_buf)
219 FD_SET(conn->fd, writefds);
221 return conn->fd + 1;
224 static void udscs_server_accept(struct udscs_server *server) {
225 struct udscs_connection *new_conn, *conn;
226 struct sockaddr_un address;
227 socklen_t address_length = sizeof(address);
228 int fd;
230 fd = accept(server->fd, (struct sockaddr *)&address, &address_length);
231 if (fd == -1) {
232 if (errno == EINTR)
233 return;
234 perror("accept");
235 return;
238 new_conn = calloc(1, sizeof(*conn));
239 if (!new_conn) {
240 fprintf(stderr, "out of memory, disconnecting client\n");
241 close(fd);
242 return;
245 new_conn->fd = fd;
246 new_conn->read_callback = server->read_callback;
247 new_conn->disconnect_callback = server->disconnect_callback;
249 conn = &server->connections_head;
250 while (conn->next)
251 conn = conn->next;
253 new_conn->prev = conn;
254 conn->next = new_conn;
257 void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
258 fd_set *writefds)
260 struct udscs_connection *conn, *next_conn;
262 if (FD_ISSET(server->fd, readfds))
263 udscs_server_accept(server);
265 conn = server->connections_head.next;
266 while (conn) {
267 /* conn maybe destroyed by udscs_client_handle_fds (when disconnected),
268 so get the next connection first. */
269 next_conn = conn->next;
270 udscs_client_handle_fds(&conn, readfds, writefds);
271 conn = next_conn;
275 void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds,
276 fd_set *writefds)
278 if (FD_ISSET((*connp)->fd, readfds))
279 udscs_do_read(connp);
281 if (*connp && FD_ISSET((*connp)->fd, writefds))
282 udscs_do_write(connp);
285 int udscs_write(struct udscs_connection *conn,
286 struct udscs_message_header *header, const uint8_t *data)
288 struct udscs_buf *wbuf, *new_wbuf;
290 new_wbuf = malloc(sizeof(*new_wbuf));
291 if (!new_wbuf)
292 return -1;
294 new_wbuf->pos = 0;
295 new_wbuf->size = sizeof(*header) + header->size;
296 new_wbuf->next = NULL;
297 new_wbuf->buf = malloc(new_wbuf->size);
298 if (!new_wbuf->buf) {
299 free(new_wbuf);
300 return -1;
303 memcpy(new_wbuf->buf, header, sizeof(*header));
304 memcpy(new_wbuf->buf + sizeof(*header), data, header->size);
306 if (!conn->write_buf) {
307 conn->write_buf = new_wbuf;
308 return 0;
311 /* FIXME maybe limit the write_buf stack depth ? */
312 wbuf = conn->write_buf;
313 while (wbuf->next)
314 wbuf = wbuf->next;
316 wbuf->next = wbuf;
318 return 0;
321 int udscs_server_write_all(struct udscs_server *server,
322 struct udscs_message_header *header, const uint8_t *data)
324 struct udscs_connection *conn;
326 conn = server->connections_head.next;
327 while (conn) {
328 if (udscs_write(conn, header, data))
329 return -1;
330 conn = conn->next;
333 return 0;
336 static void udscs_do_read(struct udscs_connection **connp)
338 ssize_t n;
339 size_t to_read;
340 uint8_t *dest;
341 int r;
342 struct udscs_connection *conn = *connp;
344 if (conn->header_read < sizeof(conn->header)) {
345 to_read = sizeof(conn->header) - conn->header_read;
346 dest = (uint8_t *)&conn->header + conn->header_read;
347 } else {
348 to_read = conn->data.size - conn->data.pos;
349 dest = conn->data.buf + conn->data.pos;
352 n = read(conn->fd, dest, to_read);
353 if (n < 0) {
354 if (errno == EINTR)
355 return;
356 perror("reading from unix domain socket");
358 if (n <= 0) {
359 udscs_destroy_connection(connp);
360 return;
363 if (conn->header_read < sizeof(conn->header)) {
364 conn->header_read += n;
365 if (conn->header_read == sizeof(conn->header)) {
366 if (conn->header.size == 0) {
367 if (conn->read_callback) {
368 r = conn->read_callback(conn, &conn->header, NULL);
369 if (r == -1) {
370 udscs_destroy_connection(connp);
371 return;
374 conn->header_read = 0;
375 } else {
376 conn->data.pos = 0;
377 conn->data.size = conn->header.size;
378 conn->data.buf = malloc(conn->data.size);
379 if (!conn->data.buf) {
380 fprintf(stderr, "out of memory, disconnecting client\n");
381 udscs_destroy_connection(connp);
382 return;
386 } else {
387 conn->data.pos += n;
388 if (conn->data.pos == conn->data.size) {
389 if (conn->read_callback) {
390 r = conn->read_callback(conn, &conn->header, conn->data.buf);
391 if (r == -1) {
392 udscs_destroy_connection(connp);
393 return;
396 free(conn->data.buf);
397 conn->header_read = 0;
398 memset(&conn->data, 0, sizeof(conn->data));
403 static void udscs_do_write(struct udscs_connection **connp)
405 ssize_t n;
406 size_t to_write;
407 struct udscs_connection *conn = *connp;
409 struct udscs_buf* wbuf = conn->write_buf;
410 if (!wbuf) {
411 fprintf(stderr,
412 "do_write called on a connection without a write buf ?!\n");
413 return;
416 to_write = wbuf->size - wbuf->pos;
417 n = write(conn->fd, wbuf->buf + wbuf->pos, to_write);
418 if (n < 0) {
419 if (errno == EINTR)
420 return;
421 perror("writing to unix domain socket");
422 udscs_destroy_connection(connp);
423 return;
426 wbuf->pos += n;
427 if (wbuf->pos == wbuf->size) {
428 conn->write_buf = wbuf->next;
429 free(wbuf->buf);
430 free(wbuf);