vdagent: Group the client and server functions together
[vd_agent/hramrach.git] / src / udscs.c
blob732db3376e5f821eb3431be97febc050457531f6
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/>.
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include "udscs.h"
36 struct udscs_buf {
37 uint8_t *buf;
38 size_t pos;
39 size_t size;
41 struct udscs_buf *next;
44 struct udscs_connection {
45 int fd;
46 const char * const *type_to_string;
47 int no_types;
48 int debug;
49 struct ucred peer_cred;
50 void *user_data;
52 /* Read stuff, single buffer, separate header and data buffer */
53 int header_read;
54 struct udscs_message_header header;
55 struct udscs_buf data;
57 /* Writes are stored in a linked list of buffers, with both the header
58 + data for a single message in 1 buffer. */
59 struct udscs_buf *write_buf;
61 /* Callbacks */
62 udscs_read_callback read_callback;
63 udscs_disconnect_callback disconnect_callback;
65 struct udscs_connection *next;
66 struct udscs_connection *prev;
69 struct udscs_connection *udscs_connect(const char *socketname,
70 udscs_read_callback read_callback,
71 udscs_disconnect_callback disconnect_callback,
72 const char * const type_to_string[], int no_types, int debug)
74 int c;
75 struct sockaddr_un address;
76 struct udscs_connection *conn;
78 conn = calloc(1, sizeof(*conn));
79 if (!conn)
80 return NULL;
82 conn->type_to_string = type_to_string;
83 conn->no_types = no_types;
84 conn->debug = debug;
86 conn->fd = socket(PF_UNIX, SOCK_STREAM, 0);
87 if (conn->fd == -1) {
88 syslog(LOG_ERR, "creating unix domain socket: %m");
89 free(conn);
90 return NULL;
93 address.sun_family = AF_UNIX;
94 snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
95 c = connect(conn->fd, (struct sockaddr *)&address, sizeof(address));
96 if (c != 0) {
97 if (conn->debug) {
98 syslog(LOG_DEBUG, "connect %s: %m", socketname);
100 free(conn);
101 return NULL;
104 conn->read_callback = read_callback;
105 conn->disconnect_callback = disconnect_callback;
107 if (conn->debug)
108 syslog(LOG_DEBUG, "%p connected to %s", conn, socketname);
110 return conn;
113 void udscs_destroy_connection(struct udscs_connection **connp)
115 struct udscs_buf *wbuf, *next_wbuf;
116 struct udscs_connection *conn = *connp;
118 if (!conn)
119 return;
121 if (conn->disconnect_callback)
122 conn->disconnect_callback(conn);
124 wbuf = conn->write_buf;
125 while (wbuf) {
126 next_wbuf = wbuf->next;
127 free(wbuf->buf);
128 free(wbuf);
129 wbuf = next_wbuf;
132 free(conn->data.buf);
134 if (conn->next)
135 conn->next->prev = conn->prev;
136 if (conn->prev)
137 conn->prev->next = conn->next;
139 close(conn->fd);
141 if (conn->debug)
142 syslog(LOG_DEBUG, "%p disconnected", conn);
144 free(conn);
145 *connp = NULL;
148 void udscs_set_user_data(struct udscs_connection *conn, void *data)
150 conn->user_data = data;
153 void *udscs_get_user_data(struct udscs_connection *conn)
155 if (!conn)
156 return NULL;
158 return conn->user_data;
161 int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
162 uint32_t arg2, const uint8_t *data, uint32_t size)
164 struct udscs_buf *wbuf, *new_wbuf;
165 struct udscs_message_header header;
167 new_wbuf = malloc(sizeof(*new_wbuf));
168 if (!new_wbuf)
169 return -1;
171 new_wbuf->pos = 0;
172 new_wbuf->size = sizeof(header) + size;
173 new_wbuf->next = NULL;
174 new_wbuf->buf = malloc(new_wbuf->size);
175 if (!new_wbuf->buf) {
176 free(new_wbuf);
177 return -1;
180 header.type = type;
181 header.arg1 = arg1;
182 header.arg2 = arg2;
183 header.size = size;
185 memcpy(new_wbuf->buf, &header, sizeof(header));
186 memcpy(new_wbuf->buf + sizeof(header), data, size);
188 if (conn->debug) {
189 if (type < conn->no_types)
190 syslog(LOG_DEBUG, "%p sent %s, arg1: %u, arg2: %u, size %u",
191 conn, conn->type_to_string[type], arg1, arg2, size);
192 else
193 syslog(LOG_DEBUG,
194 "%p sent invalid message %u, arg1: %u, arg2: %u, size %u",
195 conn, type, arg1, arg2, size);
198 if (!conn->write_buf) {
199 conn->write_buf = new_wbuf;
200 return 0;
203 /* maybe we should limit the write_buf stack depth ? */
204 wbuf = conn->write_buf;
205 while (wbuf->next)
206 wbuf = wbuf->next;
208 wbuf->next = new_wbuf;
210 return 0;
213 /* A helper for udscs_do_read() */
214 static void udscs_read_complete(struct udscs_connection **connp)
216 struct udscs_connection *conn = *connp;
218 if (conn->debug) {
219 if (conn->header.type < conn->no_types)
220 syslog(LOG_DEBUG,
221 "%p received %s, arg1: %u, arg2: %u, size %u",
222 conn, conn->type_to_string[conn->header.type],
223 conn->header.arg1, conn->header.arg2, conn->header.size);
224 else
225 syslog(LOG_DEBUG,
226 "%p received invalid message %u, arg1: %u, arg2: %u, size %u",
227 conn, conn->header.type, conn->header.arg1, conn->header.arg2,
228 conn->header.size);
231 if (conn->read_callback) {
232 conn->read_callback(connp, &conn->header, conn->data.buf);
233 if (!*connp) /* Was the connection disconnected by the callback ? */
234 return;
237 conn->header_read = 0;
238 memset(&conn->data, 0, sizeof(conn->data));
241 /* A helper for udscs_client_handle_fds() */
242 static void udscs_do_read(struct udscs_connection **connp)
244 ssize_t n;
245 size_t to_read;
246 uint8_t *dest;
247 struct udscs_connection *conn = *connp;
249 if (conn->header_read < sizeof(conn->header)) {
250 to_read = sizeof(conn->header) - conn->header_read;
251 dest = (uint8_t *)&conn->header + conn->header_read;
252 } else {
253 to_read = conn->data.size - conn->data.pos;
254 dest = conn->data.buf + conn->data.pos;
257 n = read(conn->fd, dest, to_read);
258 if (n < 0) {
259 if (errno == EINTR)
260 return;
261 syslog(LOG_ERR, "reading unix domain socket: %m, disconnecting %p",
262 conn);
264 if (n <= 0) {
265 udscs_destroy_connection(connp);
266 return;
269 if (conn->header_read < sizeof(conn->header)) {
270 conn->header_read += n;
271 if (conn->header_read == sizeof(conn->header)) {
272 if (conn->header.size == 0) {
273 udscs_read_complete(connp);
274 return;
276 conn->data.pos = 0;
277 conn->data.size = conn->header.size;
278 conn->data.buf = malloc(conn->data.size);
279 if (!conn->data.buf) {
280 syslog(LOG_ERR, "out of memory, disconnecting %p", conn);
281 udscs_destroy_connection(connp);
282 return;
285 } else {
286 conn->data.pos += n;
287 if (conn->data.pos == conn->data.size)
288 udscs_read_complete(connp);
292 /* A helper for udscs_client_handle_fds() */
293 static void udscs_do_write(struct udscs_connection **connp)
295 ssize_t n;
296 size_t to_write;
297 struct udscs_connection *conn = *connp;
299 struct udscs_buf* wbuf = conn->write_buf;
300 if (!wbuf) {
301 syslog(LOG_ERR,
302 "%p do_write called on a connection without a write buf ?!",
303 conn);
304 return;
307 to_write = wbuf->size - wbuf->pos;
308 n = write(conn->fd, wbuf->buf + wbuf->pos, to_write);
309 if (n < 0) {
310 if (errno == EINTR)
311 return;
312 syslog(LOG_ERR, "writing to unix domain socket: %m, disconnecting %p",
313 conn);
314 udscs_destroy_connection(connp);
315 return;
318 wbuf->pos += n;
319 if (wbuf->pos == wbuf->size) {
320 conn->write_buf = wbuf->next;
321 free(wbuf->buf);
322 free(wbuf);
326 void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds,
327 fd_set *writefds)
329 if (!*connp)
330 return;
332 if (FD_ISSET((*connp)->fd, readfds))
333 udscs_do_read(connp);
335 if (*connp && FD_ISSET((*connp)->fd, writefds))
336 udscs_do_write(connp);
339 int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds,
340 fd_set *writefds)
342 if (!conn)
343 return -1;
345 FD_SET(conn->fd, readfds);
346 if (conn->write_buf)
347 FD_SET(conn->fd, writefds);
349 return conn->fd + 1;
353 /* ---------- Server-side implementation ---------- */
355 struct udscs_server {
356 int fd;
357 const char * const *type_to_string;
358 int no_types;
359 int debug;
360 struct udscs_connection connections_head;
361 udscs_connect_callback connect_callback;
362 udscs_read_callback read_callback;
363 udscs_disconnect_callback disconnect_callback;
366 struct udscs_server *udscs_create_server(const char *socketname,
367 udscs_connect_callback connect_callback,
368 udscs_read_callback read_callback,
369 udscs_disconnect_callback disconnect_callback,
370 const char * const type_to_string[], int no_types, int debug)
372 int c;
373 struct sockaddr_un address;
374 struct udscs_server *server;
376 server = calloc(1, sizeof(*server));
377 if (!server)
378 return NULL;
380 server->type_to_string = type_to_string;
381 server->no_types = no_types;
382 server->debug = debug;
384 server->fd = socket(PF_UNIX, SOCK_STREAM, 0);
385 if (server->fd == -1) {
386 syslog(LOG_ERR, "creating unix domain socket: %m");
387 free(server);
388 return NULL;
391 address.sun_family = AF_UNIX;
392 snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
393 c = bind(server->fd, (struct sockaddr *)&address, sizeof(address));
394 if (c != 0) {
395 syslog(LOG_ERR, "bind %s: %m", socketname);
396 free(server);
397 return NULL;
400 c = listen(server->fd, 5);
401 if (c != 0) {
402 syslog(LOG_ERR, "listen: %m");
403 free(server);
404 return NULL;
407 server->connect_callback = connect_callback;
408 server->read_callback = read_callback;
409 server->disconnect_callback = disconnect_callback;
411 return server;
414 void udscs_destroy_server(struct udscs_server *server)
416 struct udscs_connection *conn, *next_conn;
418 if (!server)
419 return;
421 conn = server->connections_head.next;
422 while (conn) {
423 next_conn = conn->next;
424 udscs_destroy_connection(&conn);
425 conn = next_conn;
427 close(server->fd);
428 free(server);
431 struct ucred udscs_get_peer_cred(struct udscs_connection *conn)
433 return conn->peer_cred;
436 static void udscs_server_accept(struct udscs_server *server) {
437 struct udscs_connection *new_conn, *conn;
438 struct sockaddr_un address;
439 socklen_t length = sizeof(address);
440 int r, fd;
442 fd = accept(server->fd, (struct sockaddr *)&address, &length);
443 if (fd == -1) {
444 if (errno == EINTR)
445 return;
446 syslog(LOG_ERR, "accept: %m");
447 return;
450 new_conn = calloc(1, sizeof(*conn));
451 if (!new_conn) {
452 syslog(LOG_ERR, "out of memory, disconnecting new client");
453 close(fd);
454 return;
457 new_conn->fd = fd;
458 new_conn->type_to_string = server->type_to_string;
459 new_conn->no_types = server->no_types;
460 new_conn->debug = server->debug;
461 new_conn->read_callback = server->read_callback;
462 new_conn->disconnect_callback = server->disconnect_callback;
464 length = sizeof(new_conn->peer_cred);
465 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &new_conn->peer_cred, &length);
466 if (r != 0) {
467 syslog(LOG_ERR, "Could not get peercred, disconnecting new client");
468 close(fd);
469 free(new_conn);
470 return;
473 conn = &server->connections_head;
474 while (conn->next)
475 conn = conn->next;
477 new_conn->prev = conn;
478 conn->next = new_conn;
480 if (server->debug)
481 syslog(LOG_DEBUG, "new client accepted: %p, pid: %d",
482 new_conn, (int)new_conn->peer_cred.pid);
484 if (server->connect_callback)
485 server->connect_callback(new_conn);
488 int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
489 fd_set *writefds)
491 struct udscs_connection *conn;
492 int nfds = server->fd + 1;
494 if (!server)
495 return -1;
497 FD_SET(server->fd, readfds);
499 conn = server->connections_head.next;
500 while (conn) {
501 int conn_nfds = udscs_client_fill_fds(conn, readfds, writefds);
502 if (conn_nfds > nfds)
503 nfds = conn_nfds;
505 conn = conn->next;
508 return nfds;
511 void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
512 fd_set *writefds)
514 struct udscs_connection *conn, *next_conn;
516 if (!server)
517 return;
519 if (FD_ISSET(server->fd, readfds))
520 udscs_server_accept(server);
522 conn = server->connections_head.next;
523 while (conn) {
524 /* conn maybe destroyed by udscs_client_handle_fds (when disconnected),
525 so get the next connection first. */
526 next_conn = conn->next;
527 udscs_client_handle_fds(&conn, readfds, writefds);
528 conn = next_conn;
532 int udscs_server_write_all(struct udscs_server *server,
533 uint32_t type, uint32_t arg1, uint32_t arg2,
534 const uint8_t *data, uint32_t size)
536 struct udscs_connection *conn;
538 conn = server->connections_head.next;
539 while (conn) {
540 if (udscs_write(conn, type, arg1, arg2, data, size))
541 return -1;
542 conn = conn->next;
545 return 0;
548 int udscs_server_for_all_clients(struct udscs_server *server,
549 udscs_for_all_clients_callback func, void *priv)
551 int r = 0;
552 struct udscs_connection *conn, *next_conn;
554 if (!server)
555 return 0;
557 conn = server->connections_head.next;
558 while (conn) {
559 /* Get next conn as func may destroy the current conn */
560 next_conn = conn->next;
561 r += func(&conn, priv);
562 conn = next_conn;
564 return r;