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/>.
29 #include <sys/socket.h>
38 struct udscs_buf
*next
;
41 struct udscs_connection
{
44 /* Read stuff, single buffer, separate header and data buffer */
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
;
54 udscs_read_callback read_callback
;
55 udscs_disconnect_callback disconnect_callback
;
57 struct udscs_connection
*next
;
58 struct udscs_connection
*prev
;
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
)
77 struct sockaddr_un address
;
78 struct udscs_server
*server
;
80 server
= calloc(1, sizeof(*server
));
84 server
->fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
85 if (server
->fd
== -1) {
86 perror("creating unix domain socket");
91 c
= unlink(socketname
);
92 if (c
!= 0 && errno
!= ENOENT
) {
93 fprintf(stderr
, "unlink %s: %s\n", socketname
, strerror(errno
));
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
));
102 fprintf(stderr
, "bind %s: %s\n", socketname
, strerror(errno
));
107 c
= listen(server
->fd
, 5);
114 server
->read_callback
= read_callback
;
115 server
->disconnect_callback
= disconnect_callback
;
120 void udscs_destroy_server(struct udscs_server
*server
)
122 struct udscs_connection
*conn
, *next_conn
;
124 conn
= server
->connections_head
.next
;
126 next_conn
= conn
->next
;
127 udscs_destroy_connection(&conn
);
134 struct udscs_connection
*udscs_connect(const char *socketname
,
135 udscs_read_callback read_callback
,
136 udscs_disconnect_callback disconnect_callback
)
139 struct sockaddr_un address
;
140 struct udscs_connection
*conn
;
142 conn
= calloc(1, sizeof(*conn
));
146 conn
->fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
147 if (conn
->fd
== -1) {
148 perror("creating unix domain socket");
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
));
157 fprintf(stderr
, "connect %s: %s\n", socketname
, strerror(errno
));
162 conn
->read_callback
= read_callback
;
163 conn
->disconnect_callback
= disconnect_callback
;
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
;
178 next_wbuf
= wbuf
->next
;
184 free(conn
->data
.buf
);
187 conn
->prev
->next
= conn
->next
;
194 int udscs_server_fill_fds(struct udscs_server
*server
, fd_set
*readfds
,
197 struct udscs_connection
*conn
;
198 int nfds
= server
->fd
+ 1;
200 FD_SET(server
->fd
, readfds
);
202 conn
= server
->connections_head
.next
;
204 int conn_nfds
= udscs_client_fill_fds(conn
, readfds
, writefds
);
205 if (conn_nfds
> nfds
)
214 int udscs_client_fill_fds(struct udscs_connection
*conn
, fd_set
*readfds
,
217 FD_SET(conn
->fd
, readfds
);
219 FD_SET(conn
->fd
, writefds
);
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
);
230 fd
= accept(server
->fd
, (struct sockaddr
*)&address
, &address_length
);
238 new_conn
= calloc(1, sizeof(*conn
));
240 fprintf(stderr
, "out of memory, disconnecting client\n");
246 new_conn
->read_callback
= server
->read_callback
;
247 new_conn
->disconnect_callback
= server
->disconnect_callback
;
249 conn
= &server
->connections_head
;
253 new_conn
->prev
= conn
;
254 conn
->next
= new_conn
;
257 void udscs_server_handle_fds(struct udscs_server
*server
, fd_set
*readfds
,
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
;
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
);
275 void udscs_client_handle_fds(struct udscs_connection
**connp
, fd_set
*readfds
,
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
));
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
) {
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
;
311 /* FIXME maybe limit the write_buf stack depth ? */
312 wbuf
= conn
->write_buf
;
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
;
328 if (udscs_write(conn
, header
, data
))
336 static void udscs_do_read(struct udscs_connection
**connp
)
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
;
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
);
356 perror("reading from unix domain socket");
359 udscs_destroy_connection(connp
);
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
);
370 udscs_destroy_connection(connp
);
374 conn
->header_read
= 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
);
388 if (conn
->data
.pos
== conn
->data
.size
) {
389 if (conn
->read_callback
) {
390 r
= conn
->read_callback(conn
, &conn
->header
, conn
->data
.buf
);
392 udscs_destroy_connection(connp
);
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
)
407 struct udscs_connection
*conn
= *connp
;
409 struct udscs_buf
* wbuf
= conn
->write_buf
;
412 "do_write called on a connection without a write buf ?!\n");
416 to_write
= wbuf
->size
- wbuf
->pos
;
417 n
= write(conn
->fd
, wbuf
->buf
+ wbuf
->pos
, to_write
);
421 perror("writing to unix domain socket");
422 udscs_destroy_connection(connp
);
427 if (wbuf
->pos
== wbuf
->size
) {
428 conn
->write_buf
= wbuf
->next
;