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.
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/>.
32 #include <sys/socket.h>
41 struct udscs_buf
*next
;
44 struct udscs_connection
{
46 const char * const *type_to_string
;
50 #ifndef UDSCS_NO_SERVER
51 struct ucred peer_cred
;
54 /* Read stuff, single buffer, separate header and data buffer */
56 struct udscs_message_header header
;
57 struct udscs_buf data
;
59 /* Writes are stored in a linked list of buffers, with both the header
60 + data for a single message in 1 buffer. */
61 struct udscs_buf
*write_buf
;
64 udscs_read_callback read_callback
;
65 udscs_disconnect_callback disconnect_callback
;
67 struct udscs_connection
*next
;
68 struct udscs_connection
*prev
;
71 struct udscs_connection
*udscs_connect(const char *socketname
,
72 udscs_read_callback read_callback
,
73 udscs_disconnect_callback disconnect_callback
,
74 const char * const type_to_string
[], int no_types
, int debug
)
77 struct sockaddr_un address
;
78 struct udscs_connection
*conn
;
80 conn
= calloc(1, sizeof(*conn
));
84 conn
->type_to_string
= type_to_string
;
85 conn
->no_types
= no_types
;
88 conn
->fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
90 syslog(LOG_ERR
, "creating unix domain socket: %m");
95 address
.sun_family
= AF_UNIX
;
96 snprintf(address
.sun_path
, sizeof(address
.sun_path
), "%s", socketname
);
97 c
= connect(conn
->fd
, (struct sockaddr
*)&address
, sizeof(address
));
100 syslog(LOG_DEBUG
, "connect %s: %m", socketname
);
106 conn
->read_callback
= read_callback
;
107 conn
->disconnect_callback
= disconnect_callback
;
110 syslog(LOG_DEBUG
, "%p connected to %s", conn
, socketname
);
115 void udscs_destroy_connection(struct udscs_connection
**connp
)
117 struct udscs_buf
*wbuf
, *next_wbuf
;
118 struct udscs_connection
*conn
= *connp
;
123 if (conn
->disconnect_callback
)
124 conn
->disconnect_callback(conn
);
126 wbuf
= conn
->write_buf
;
128 next_wbuf
= wbuf
->next
;
134 free(conn
->data
.buf
);
135 conn
->data
.buf
= NULL
;
138 conn
->next
->prev
= conn
->prev
;
140 conn
->prev
->next
= conn
->next
;
145 syslog(LOG_DEBUG
, "%p disconnected", conn
);
151 void udscs_set_user_data(struct udscs_connection
*conn
, void *data
)
153 conn
->user_data
= data
;
156 void *udscs_get_user_data(struct udscs_connection
*conn
)
161 return conn
->user_data
;
164 int udscs_write(struct udscs_connection
*conn
, uint32_t type
, uint32_t arg1
,
165 uint32_t arg2
, const uint8_t *data
, uint32_t size
)
167 struct udscs_buf
*wbuf
, *new_wbuf
;
168 struct udscs_message_header header
;
170 new_wbuf
= malloc(sizeof(*new_wbuf
));
175 new_wbuf
->size
= sizeof(header
) + size
;
176 new_wbuf
->next
= NULL
;
177 new_wbuf
->buf
= malloc(new_wbuf
->size
);
178 if (!new_wbuf
->buf
) {
188 memcpy(new_wbuf
->buf
, &header
, sizeof(header
));
189 memcpy(new_wbuf
->buf
+ sizeof(header
), data
, size
);
192 if (type
< conn
->no_types
)
193 syslog(LOG_DEBUG
, "%p sent %s, arg1: %u, arg2: %u, size %u",
194 conn
, conn
->type_to_string
[type
], arg1
, arg2
, size
);
197 "%p sent invalid message %u, arg1: %u, arg2: %u, size %u",
198 conn
, type
, arg1
, arg2
, size
);
201 if (!conn
->write_buf
) {
202 conn
->write_buf
= new_wbuf
;
206 /* maybe we should limit the write_buf stack depth ? */
207 wbuf
= conn
->write_buf
;
211 wbuf
->next
= new_wbuf
;
216 /* A helper for udscs_do_read() */
217 static void udscs_read_complete(struct udscs_connection
**connp
)
219 struct udscs_connection
*conn
= *connp
;
222 if (conn
->header
.type
< conn
->no_types
)
224 "%p received %s, arg1: %u, arg2: %u, size %u",
225 conn
, conn
->type_to_string
[conn
->header
.type
],
226 conn
->header
.arg1
, conn
->header
.arg2
, conn
->header
.size
);
229 "%p received invalid message %u, arg1: %u, arg2: %u, size %u",
230 conn
, conn
->header
.type
, conn
->header
.arg1
, conn
->header
.arg2
,
234 if (conn
->read_callback
) {
235 conn
->read_callback(connp
, &conn
->header
, conn
->data
.buf
);
236 if (!*connp
) /* Was the connection disconnected by the callback ? */
240 free(conn
->data
.buf
);
241 memset(&conn
->data
, 0, sizeof(conn
->data
)); /* data.buf = NULL */
242 conn
->header_read
= 0;
245 /* A helper for udscs_client_handle_fds() */
246 static void udscs_do_read(struct udscs_connection
**connp
)
251 struct udscs_connection
*conn
= *connp
;
253 if (conn
->header_read
< sizeof(conn
->header
)) {
254 to_read
= sizeof(conn
->header
) - conn
->header_read
;
255 dest
= (uint8_t *)&conn
->header
+ conn
->header_read
;
257 to_read
= conn
->data
.size
- conn
->data
.pos
;
258 dest
= conn
->data
.buf
+ conn
->data
.pos
;
261 n
= read(conn
->fd
, dest
, to_read
);
265 syslog(LOG_ERR
, "reading unix domain socket: %m, disconnecting %p",
269 udscs_destroy_connection(connp
);
273 if (conn
->header_read
< sizeof(conn
->header
)) {
274 conn
->header_read
+= n
;
275 if (conn
->header_read
== sizeof(conn
->header
)) {
276 if (conn
->header
.size
== 0) {
277 udscs_read_complete(connp
);
281 conn
->data
.size
= conn
->header
.size
;
282 conn
->data
.buf
= malloc(conn
->data
.size
);
283 if (!conn
->data
.buf
) {
284 syslog(LOG_ERR
, "out of memory, disconnecting %p", conn
);
285 udscs_destroy_connection(connp
);
291 if (conn
->data
.pos
== conn
->data
.size
)
292 udscs_read_complete(connp
);
296 /* A helper for udscs_client_handle_fds() */
297 static void udscs_do_write(struct udscs_connection
**connp
)
301 struct udscs_connection
*conn
= *connp
;
303 struct udscs_buf
* wbuf
= conn
->write_buf
;
306 "%p do_write called on a connection without a write buf ?!",
311 to_write
= wbuf
->size
- wbuf
->pos
;
312 n
= write(conn
->fd
, wbuf
->buf
+ wbuf
->pos
, to_write
);
316 syslog(LOG_ERR
, "writing to unix domain socket: %m, disconnecting %p",
318 udscs_destroy_connection(connp
);
323 if (wbuf
->pos
== wbuf
->size
) {
324 conn
->write_buf
= wbuf
->next
;
330 void udscs_client_handle_fds(struct udscs_connection
**connp
, fd_set
*readfds
,
336 if (FD_ISSET((*connp
)->fd
, readfds
))
337 udscs_do_read(connp
);
339 if (*connp
&& FD_ISSET((*connp
)->fd
, writefds
))
340 udscs_do_write(connp
);
343 int udscs_client_fill_fds(struct udscs_connection
*conn
, fd_set
*readfds
,
349 FD_SET(conn
->fd
, readfds
);
351 FD_SET(conn
->fd
, writefds
);
357 #ifndef UDSCS_NO_SERVER
359 /* ---------- Server-side implementation ---------- */
361 struct udscs_server
{
363 const char * const *type_to_string
;
366 struct udscs_connection connections_head
;
367 udscs_connect_callback connect_callback
;
368 udscs_read_callback read_callback
;
369 udscs_disconnect_callback disconnect_callback
;
372 struct udscs_server
*udscs_create_server(const char *socketname
,
373 udscs_connect_callback connect_callback
,
374 udscs_read_callback read_callback
,
375 udscs_disconnect_callback disconnect_callback
,
376 const char * const type_to_string
[], int no_types
, int debug
)
379 struct sockaddr_un address
;
380 struct udscs_server
*server
;
382 server
= calloc(1, sizeof(*server
));
386 server
->type_to_string
= type_to_string
;
387 server
->no_types
= no_types
;
388 server
->debug
= debug
;
390 server
->fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
391 if (server
->fd
== -1) {
392 syslog(LOG_ERR
, "creating unix domain socket: %m");
397 address
.sun_family
= AF_UNIX
;
398 snprintf(address
.sun_path
, sizeof(address
.sun_path
), "%s", socketname
);
399 c
= bind(server
->fd
, (struct sockaddr
*)&address
, sizeof(address
));
401 syslog(LOG_ERR
, "bind %s: %m", socketname
);
406 c
= listen(server
->fd
, 5);
408 syslog(LOG_ERR
, "listen: %m");
413 server
->connect_callback
= connect_callback
;
414 server
->read_callback
= read_callback
;
415 server
->disconnect_callback
= disconnect_callback
;
420 void udscs_destroy_server(struct udscs_server
*server
)
422 struct udscs_connection
*conn
, *next_conn
;
427 conn
= server
->connections_head
.next
;
429 next_conn
= conn
->next
;
430 udscs_destroy_connection(&conn
);
437 struct ucred
udscs_get_peer_cred(struct udscs_connection
*conn
)
439 return conn
->peer_cred
;
442 static void udscs_server_accept(struct udscs_server
*server
) {
443 struct udscs_connection
*new_conn
, *conn
;
444 struct sockaddr_un address
;
445 socklen_t length
= sizeof(address
);
448 fd
= accept(server
->fd
, (struct sockaddr
*)&address
, &length
);
452 syslog(LOG_ERR
, "accept: %m");
456 new_conn
= calloc(1, sizeof(*conn
));
458 syslog(LOG_ERR
, "out of memory, disconnecting new client");
464 new_conn
->type_to_string
= server
->type_to_string
;
465 new_conn
->no_types
= server
->no_types
;
466 new_conn
->debug
= server
->debug
;
467 new_conn
->read_callback
= server
->read_callback
;
468 new_conn
->disconnect_callback
= server
->disconnect_callback
;
470 length
= sizeof(new_conn
->peer_cred
);
471 r
= getsockopt(fd
, SOL_SOCKET
, SO_PEERCRED
, &new_conn
->peer_cred
, &length
);
473 syslog(LOG_ERR
, "Could not get peercred, disconnecting new client");
479 conn
= &server
->connections_head
;
483 new_conn
->prev
= conn
;
484 conn
->next
= new_conn
;
487 syslog(LOG_DEBUG
, "new client accepted: %p, pid: %d",
488 new_conn
, (int)new_conn
->peer_cred
.pid
);
490 if (server
->connect_callback
)
491 server
->connect_callback(new_conn
);
494 int udscs_server_fill_fds(struct udscs_server
*server
, fd_set
*readfds
,
497 struct udscs_connection
*conn
;
503 nfds
= server
->fd
+ 1;
504 FD_SET(server
->fd
, readfds
);
506 conn
= server
->connections_head
.next
;
508 int conn_nfds
= udscs_client_fill_fds(conn
, readfds
, writefds
);
509 if (conn_nfds
> nfds
)
518 void udscs_server_handle_fds(struct udscs_server
*server
, fd_set
*readfds
,
521 struct udscs_connection
*conn
, *next_conn
;
526 if (FD_ISSET(server
->fd
, readfds
))
527 udscs_server_accept(server
);
529 conn
= server
->connections_head
.next
;
531 /* conn maybe destroyed by udscs_client_handle_fds (when disconnected),
532 so get the next connection first. */
533 next_conn
= conn
->next
;
534 udscs_client_handle_fds(&conn
, readfds
, writefds
);
539 int udscs_server_write_all(struct udscs_server
*server
,
540 uint32_t type
, uint32_t arg1
, uint32_t arg2
,
541 const uint8_t *data
, uint32_t size
)
543 struct udscs_connection
*conn
;
545 conn
= server
->connections_head
.next
;
547 if (udscs_write(conn
, type
, arg1
, arg2
, data
, size
))
555 int udscs_server_for_all_clients(struct udscs_server
*server
,
556 udscs_for_all_clients_callback func
, void *priv
)
559 struct udscs_connection
*conn
, *next_conn
;
564 conn
= server
->connections_head
.next
;
566 /* Get next conn as func may destroy the current conn */
567 next_conn
= conn
->next
;
568 r
+= func(&conn
, priv
);